[exec.ctx] # 33 Execution control library [[exec]](./#exec) ## 33.12 Execution contexts [exec.ctx] ### [33.12.1](#exec.run.loop) execution​::​run_loop [[exec.run.loop]](exec.run.loop) #### [33.12.1.1](#exec.run.loop.general) General [[exec.run.loop.general]](exec.run.loop.general) [1](#exec.run.loop.general-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6321) A run_loop is an execution resource on which work can be scheduled[.](#exec.run.loop.general-1.sentence-1) It maintains a thread-safe first-in-first-out queue of work[.](#exec.run.loop.general-1.sentence-2) Its run member function removes elements from the queue and executes them in a loop on the thread of execution that calls run[.](#exec.run.loop.general-1.sentence-3) [2](#exec.run.loop.general-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6327) A run_loop instance has an associated [*count*](#def:count "33.12.1.1 General [exec.run.loop.general]") that corresponds to the number of work items that are in its queue[.](#exec.run.loop.general-2.sentence-1) Additionally, a run_loop instance has an associated state that can be one of[*starting*](#def:starting "33.12.1.1 General [exec.run.loop.general]"), [*running*](#def:running "33.12.1.1 General [exec.run.loop.general]"), [*finishing*](#def:finishing "33.12.1.1 General [exec.run.loop.general]"), or [*finished*](#def:finished "33.12.1.1 General [exec.run.loop.general]")[.](#exec.run.loop.general-2.sentence-2) [3](#exec.run.loop.general-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6334) Concurrent invocations of the member functions of run_loop other than run and its destructor do not introduce data races[.](#exec.run.loop.general-3.sentence-1) The member functions*pop-front*, *push-back*, and finish execute atomically[.](#exec.run.loop.general-3.sentence-2) [4](#exec.run.loop.general-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6341) *Recommended practice*: Implementations should use an intrusive queue of operation states to hold the work units to make scheduling allocation-free[.](#exec.run.loop.general-4.sentence-1) namespace std::execution {class [run_loop](#lib:run_loop "33.12.1.1 General [exec.run.loop.general]") {// [[exec.run.loop.types]](#exec.run.loop.types "33.12.1.2 Associated types"), associated typesclass *run-loop-scheduler*; // *exposition only*class *run-loop-sender*; // *exposition only*struct *run-loop-opstate-base* { // *exposition only*virtual void *execute*() = 0; // *exposition only* run_loop* *loop*; // *exposition only**run-loop-opstate-base** *next*; // *exposition only*}; templateusing *run-loop-opstate* = *unspecified*; // *exposition only*// [[exec.run.loop.members]](#exec.run.loop.members "33.12.1.4 Member functions"), member functions*run-loop-opstate-base** *pop-front*(); // *exposition only*void *push-back*(*run-loop-opstate-base**); // *exposition only*public:// [[exec.run.loop.ctor]](#exec.run.loop.ctor "33.12.1.3 Constructor and destructor"), constructor and destructor run_loop() noexcept; run_loop(run_loop&&) = delete; ~run_loop(); // [[exec.run.loop.members]](#exec.run.loop.members "33.12.1.4 Member functions"), member functions*run-loop-scheduler* get_scheduler(); void run(); void finish(); };} #### [33.12.1.2](#exec.run.loop.types) Associated types [[exec.run.loop.types]](exec.run.loop.types) [🔗](#exec.run.loop.types-itemdecl:1) `class run-loop-scheduler; ` [1](#exec.run.loop.types-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6384) *run-loop-scheduler* is an unspecified type that models [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]")[.](#exec.run.loop.types-1.sentence-1) [2](#exec.run.loop.types-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6388) Instances of *run-loop-scheduler* remain valid until the end of the lifetime of the run_loop instance from which they were obtained[.](#exec.run.loop.types-2.sentence-1) [3](#exec.run.loop.types-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6393) Two instances of *run-loop-scheduler* compare equal if and only if they were obtained from the same run_loop instance[.](#exec.run.loop.types-3.sentence-1) [4](#exec.run.loop.types-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6397) Let *sch* be an expression of type *run-loop-scheduler*[.](#exec.run.loop.types-4.sentence-1) The expression schedule(*sch*) has type *run-loop- sender* and is not potentially-throwing if *sch* is not potentially-throwing[.](#exec.run.loop.types-4.sentence-2) [🔗](#exec.run.loop.types-itemdecl:2) `class run-loop-sender; ` [5](#exec.run.loop.types-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6407) *run-loop-sender* is an exposition-only type that satisfies [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.run.loop.types-5.sentence-1) completion_signatures_of_t<*run- loop-sender*> iscompletion_signatures [6](#exec.run.loop.types-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6415) An instance of *run-loop-sender* remains valid until the end of the lifetime of its associated run_loop instance[.](#exec.run.loop.types-6.sentence-1) [7](#exec.run.loop.types-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6419) Let *sndr* be an expression of type *run-loop-sender*, let *rcvr* be an expression such that [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1 Receiver concepts [exec.recv.concepts]") is true where CS is the completion_signatures specialization above[.](#exec.run.loop.types-7.sentence-1) Let C be either set_value_t or set_stopped_t[.](#exec.run.loop.types-7.sentence-2) Then: - [(7.1)](#exec.run.loop.types-7.1) The expression connect(*sndr*, *rcvr*) has type *run-loop-opstate*> and is potentially-throwing if and only if(void(*sndr*), auto(*rcvr*)) is potentially-throwing[.](#exec.run.loop.types-7.1.sentence-1) - [(7.2)](#exec.run.loop.types-7.2) The expression get_completion_scheduler(get_env(*sndr*)) is potentially-throwing if and only if *sndr* is potentially-throwing, has type *run-loop-scheduler*, and compares equal to the *run-loop- scheduler* instance from which *sndr* was obtained[.](#exec.run.loop.types-7.2.sentence-1) [🔗](#exec.run.loop.types-itemdecl:3) `template struct run-loop-opstate; ` [8](#exec.run.loop.types-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6445) *run-loop-opstate* inherits privately and unambiguously from *run-loop-opstate-base*[.](#exec.run.loop.types-8.sentence-1) [9](#exec.run.loop.types-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6449) Let o be a non-const lvalue of type *run-loop-opstate*, and let REC(o) be a non-const lvalue reference to an instance of type Rcvr that was initialized with the expression *rcvr* passed to the invocation of connect that returned o[.](#exec.run.loop.types-9.sentence-1) Then: - [(9.1)](#exec.run.loop.types-9.1) The object to which *REC*(o) refers remains valid for the lifetime of the object to which o refers. - [(9.2)](#exec.run.loop.types-9.2) The type *run-loop-opstate* overrides*run-loop-opstate-base*​::​*execute*() such that o.*execute*() is equivalent to:if (get_stop_token(*REC*(o)).stop_requested()) { set_stopped(std::move(*REC*(o)));} else { set_value(std::move(*REC*(o)));} - [(9.3)](#exec.run.loop.types-9.3) The expression start(o) is equivalent to:try {o.*loop*->*push-back*(addressof(o));} catch(...) { set_error(std::move(*REC*(o)), current_exception());} #### [33.12.1.3](#exec.run.loop.ctor) Constructor and destructor [[exec.run.loop.ctor]](exec.run.loop.ctor) [🔗](#lib:run_loop,constructor) `run_loop() noexcept; ` [1](#exec.run.loop.ctor-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6489) *Postconditions*: *count* is 0 and *state* is *starting*[.](#exec.run.loop.ctor-1.sentence-1) [🔗](#lib:run_loop,destructor) `~run_loop(); ` [2](#exec.run.loop.ctor-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6500) *Effects*: If *count* is not 0 or if *state* is *running*, invokes terminate ([[except.terminate]](except.terminate "14.6.2 The std​::​terminate function"))[.](#exec.run.loop.ctor-2.sentence-1) Otherwise, has no effects[.](#exec.run.loop.ctor-2.sentence-2) #### [33.12.1.4](#exec.run.loop.members) Member functions [[exec.run.loop.members]](exec.run.loop.members) [🔗](#exec.run.loop.members-itemdecl:1) `run-loop-opstate-base* pop-front(); ` [1](#exec.run.loop.members-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6514) *Effects*: Blocks ([[defns.block]](defns.block "3.6 block")) until one of the following conditions is true: - [(1.1)](#exec.run.loop.members-1.1) *count* is 0 and *state* is *finishing*, in which case *pop-front* sets *state* to *finished* and returns nullptr; or - [(1.2)](#exec.run.loop.members-1.2) *count* is greater than 0, in which case an item is removed from the front of the queue,*count* is decremented by 1, and the removed item is returned[.](#exec.run.loop.members-1.sentence-1) [🔗](#exec.run.loop.members-itemdecl:2) `void push-back(run-loop-opstate-base* item); ` [2](#exec.run.loop.members-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6535) *Effects*: Adds item to the back of the queue and increments *count* by 1[.](#exec.run.loop.members-2.sentence-1) [3](#exec.run.loop.members-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6540) *Synchronization*: This operation synchronizes with the *pop-front* operation that obtains item[.](#exec.run.loop.members-3.sentence-1) [🔗](#lib:get_scheduler,run_loop) `run-loop-scheduler get_scheduler(); ` [4](#exec.run.loop.members-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6552) *Returns*: An instance of *run-loop-scheduler* that can be used to schedule work onto this run_loop instance[.](#exec.run.loop.members-4.sentence-1) [🔗](#lib:run,run_loop) `void run(); ` [5](#exec.run.loop.members-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6564) *Preconditions*: *state* is either *starting* or *finishing*[.](#exec.run.loop.members-5.sentence-1) [6](#exec.run.loop.members-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6568) *Effects*: If *state* is *starting*, sets the *state* to *running*, otherwise leaves *state* unchanged[.](#exec.run.loop.members-6.sentence-1) Then, equivalent to:while (auto* op = *pop-front*()) { op->*execute*();} [7](#exec.run.loop.members-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6580) *Remarks*: When *state* changes, it does so without introducing data races[.](#exec.run.loop.members-7.sentence-1) [🔗](#lib:finish,run_loop) `void finish(); ` [8](#exec.run.loop.members-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6591) *Preconditions*: *state* is either *starting* or *running*[.](#exec.run.loop.members-8.sentence-1) [9](#exec.run.loop.members-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6595) *Effects*: Changes *state* to *finishing*[.](#exec.run.loop.members-9.sentence-1) [10](#exec.run.loop.members-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6599) *Synchronization*: finish synchronizes with the *pop-front* operation that returns nullptr[.](#exec.run.loop.members-10.sentence-1)