Back to Arangodb

Class execution_context (version 2)

3rdParty/boost/1.78.0/libs/context/doc/html/context/ecv2.html

3.12.1116.4 KB
Original Source

| | Home | Libraries | People | FAQ | More |


Class execution_context (version 2)

| | Note | |

execution_context (v2) is the reference implementation of C++ proposal P099R1: A low-level API for stackful context switching.

|

| | Note | |

execution_context (v2) resides in the inlined sub-namespace v2.

|

| | Note | |

Segmented stacks (segmented-stacks=on), e.g. on demand growing stacks, are not supported by execution_context (v2).

|

Class execution_context encapsulates context switching and manages the associated context' stack (allocation/deallocation).

execution_context allocates the context stack (using its StackAllocator argument) and creates a control structure on top of it. This structure is responsible for managing context' stack. The address of the control structure is stored in the first frame of context' stack (e.g. it can not directly accessed from within execution_context). In contrast to execution_context (v1) the ownership of the control structure is not shared (no member variable to control structure in execution_context). execution_context keeps internally a state that is moved by a call of execution_context::operator() (*this will be invalidated), e.g. after a calling execution_context::operator(), *this can not be used for an additional context switch.

execution_context is only move-constructible and move-assignable.

The moved state is assigned to a new instance of execution_context. This object becomes the first argument of the context-function, if the context was resumed the first time, or the first element in a tuple returned by execution_context::operator() that has been called in the resumed context. In contrast to execution_context (v1), the context switch is faster because no global pointer etc. is involved.

| | Important | |

Segmented stacks are not supported by execution_context (v2).

|

On return the context-function of the current context has to specify an execution_context to which the execution control is transferred after termination of the current context.

If an instance with valid state goes out of scope and the context-function has not yet returned, the stack is traversed in order to access the control structure (address stored at the first stack frame) and context' stack is deallocated via the StackAllocator. The stack walking makes the destruction of execution_context slow and should be prevented if possible.

execution_context expects a context-function with signature execution_context(execution_context ctx, Args ... args). The parameter ctx represents the context from which this context was resumed (e.g. that has called execution_context::operator() on *this) and args are the data passed to execution_context::operator(). The return value represents the execution_context that has to be resumed, after termiantion of this context.

Benefits of execution_context (v2) over execution_context (v1) are: faster context switch, type-safety of passed/returned arguments.

[usage of execution_context](ecv2.html#context.ecv2.usage_of emphasis_execution_context emphasis_)

intn=35;ctx::execution\_context\<int\>source([n](ctx::execution\_context\<int\>&&sink,int)mutable{inta=0;intb=1;while(n--\>0){autoresult=sink(a);sink=std::move(std::get\<0\>(result));autonext=a+b;a=b;b=next;}returnstd::move(sink);});for(inti=0;i\<10;++i){autoresult=source(i);source=std::move(std::get\<0\>(result));std::cout\<\<std::get\<1\>(result)\<\<" ";}output:0112358132134

This simple example demonstrates the basic usage of execution_context as a generator. The context sink represents the main-context (function main() running). sink is generated by the framework (first element of lambda's parameter list). Because the state is invalidated (== changed) by each call of execution_context::operator(), the new state of the execution_context, returned by execution_context::operator(), needs to be assigned to sink after each call.

The lambda that calculates the Fibonacci numbers is executed inside the context represented by source. Calculated Fibonacci numbers are transferred between the two context' via expression sink(a) (and returned by source()). Note that this example represents a generator thus the value transferred into the lambda via source() is not used. Using boost::optional<> as transferred type, might also appropriate to express this fact.

The locale variables a, b and next remain their values during each context switch (yield(a)). This is possible due source has its own stack and the stack is exchanged by each context switch.

parameter passing

With execution_context<void> no data will be transferred, only the context switch is executed.

boost::context::execution\_context\<void\>ctx1([](boost::context::execution\_context\<void\>&&ctx2){std::printf("inside ctx1\n");returnctx2();});ctx1();output:insidectx1

ctx1() resumes ctx1, e.g. the lambda passed at the constructor of ctx1 is entered. Argument ctx2 represents the context that has been suspended with the invocation of ctx1(). When the lambda returns ctx2, context ctx1 will be terminated while the context represented by ctx2 is resumed, hence the control of execution returns from ctx1().

The arguments passed to execution_context::operator(), in one context, is passed as the last arguments of the context-function if the context is started for the first time. In all following invocations of execution_context::operator() the arguments passed to execution_context::operator(), in one context, is returned by execution_context::operator() in the other context.

boost::context::execution\_context\<int\>ctx1([](boost::context::execution\_context\<int\>&&ctx2,intj){std::printf("inside ctx1,j==%d\n",j);std::tie(ctx2,j)=ctx2(j+1);returnstd::move(ctx2);});inti=1;std::tie(ctx1,i)=ctx1(i);std::printf("i==%d\n",i);output:insidectx1,j==1i==2

ctx1(i) enters the lambda in context ctx1 with argument j=1. The expression ctx2(j+1) resumes the context represented by ctx2 and transfers back an integer of j+1. On return of ctx1(i), the variable i contains the value of j+1.

If more than one argument has to be transferred, the signature of the context-function is simply extended.

boost::context::execution\_context\<int,int\>ctx1([](boost::context::execution\_context\<int,int\>&&ctx2,inti,intj){std::printf("inside ctx1,i==%d,j==%d\n",i,j);std::tie(ctx2,i,j)=ctx2(i+j,i-j);returnstd::move(ctx2);});inti=2,j=1;std::tie(ctx1,i,j)=ctx1(i,j);std::printf("i==%d,j==%d\n",i,j);output:insidectx1,i==2,j==1i==3,j==1

For use-cases, that require to transfer data of different type in each direction, boost::variant<> could be used.

classX{private:std::exception\_ptrexcptr\_;boost::context::execution\_context\<boost::variant\<int,std::string\>\>ctx\_;public:X():excptr\_(),ctx\_([=](boost::context::execution\_context\<boost::variant\<int,std::string\>\>&&ctx,boost::variant\<int,std::string\>data){try{for(;;){inti=boost::get\<int\>(data);data=boost::lexical\_cast\<std::string\>(i);autoresult=ctx(data);ctx=std::move(std::get\<0\>(result));data=std::get\<1\>(result);}catch(std::bad\_castconst&){excptr\_=std::current\_exception();}returnstd::move(ctx);}){}std::stringoperator()(inti){boost::variant\<int,std::string\>data=i;autoresult=ctx\_(data);ctx\_=std::move(std::get\<0\>(result));data=std::get\<1\>(result);if(excptr\_){std::rethrow\_exception(excptr\_);}returnboost::get\<std::string\>(data);}};Xx;std::cout\<\<x(7)\<\<std::endl;output:7

In the case of unidirectional transfer of data, boost::optional<> or a pointer are appropriate.

exception handling

If the function executed inside a execution_context emits an exception, the application is terminated by calling std::terminate(). std::exception_ptr can be used to transfer exceptions between different execution contexts.

| | Important | |

Do not jump from inside a catch block and then re-throw the exception in another execution context.

|

Executing function on top of a context

Sometimes it is useful to execute a new function on top of a resumed context. For this purpose execution_context::operator() with first argument exec_ontop_arg has to be used. The function passed as argument must return a tuple of execution_context and arguments.

boost::context::execution\_context\<int\>f1(boost::context::execution\_context\<int\>&&ctx,intdata){std::cout\<\<"f1: entered first time: "\<\<data\<\<std::endl;std::tie(ctx,data)=ctx(data+1);std::cout\<\<"f1: entered second time: "\<\<data\<\<std::endl;std::tie(ctx,data)=ctx(data+1);std::cout\<\<"f1: entered third time: "\<\<data\<\<std::endl;returnstd::move(ctx);}intf2(intdata){std::cout\<\<"f2: entered: "\<\<data\<\<std::endl;return-1;}intdata=0;boost::context::execution\_context\<int\>ctx(f1);std::tie(ctx,data)=ctx(data+1);std::cout\<\<"f1: returned first time: "\<\<data\<\<std::endl;std::tie(ctx,data)=ctx(data+1);std::cout\<\<"f1: returned second time: "\<\<data\<\<std::endl;std::tie(ctx,data)=ctx(ctx::exec\_ontop\_arg,f2,data+1);output:f1:enteredfirsttime:1f1:returnedfirsttime:2f1:enteredsecondtime:3f1:returnedsecondtime:4f2:entered:5f1:enteredthirdtime:-1

The expression ctx(ctx::exec_ontop_arg,f2,data+1) executes f2() on top of context ctx, e.g. an additional stack frame is allocated on top of the context stack (in front of f1()). f2() returns argument -1 that will returned by the second invocation of ctx(data+1) in f1().

Destructor
~execution\_context();

Effects:

Destructs the associated stack if *this is a valid context, e.g. execution_context::operator bool() returns true.

Throws:

Nothing.

Move constructor
execution\_context(execution\_context&&other)noexcept;

Effects:

Moves underlying capture record to *this.

Throws:

Nothing.

Move assignment operator
execution\_context&operator=(execution\_context&&other)noexcept;

Effects:

Moves the state of other to *this using move semantics.

Throws:

Nothing.

Member function operator bool()
explicitoperatorbool()constnoexcept;

Returns:

true if *this points to a capture record.

Throws:

Nothing.

Member function operator!()
booloperator!()constnoexcept;

Returns:

true if *this does not point to a capture record.

Throws:

Nothing.

Member function operator()()
std::tuple\<execution\_context\<Args...\>,Args...\>operator()(Args...args);// member of generic execution\_context templateexecution\_context\<void\>operator()();// member of execution\_context\< void \>

Effects:

Stores internally the current context data (stack pointer, instruction pointer, and CPU registers) of the current active context and restores the context data from *this, which implies jumping to *this's context. The arguments, ... args, are passed to the current context to be returned by the most recent call to execution_context::operator() in the same thread.

Returns:

The tuple of execution_context and returned arguments passed to the most recent call to execution_context::operator(), if any and a execution_context representing the context that has been suspended.

Note:

The returned execution_context indicates if the suspended context has terminated (return from context-function) via bool operator(). If the returned execution_context has terminated no data are transferred in the returned tuple.

Member function operator()()
template\<typenameFn\>std::tuple\<execution\_context\<Args...\>,Args...\>operator()(exec\_ontop\_arg\_t,Fn&&fn,Args...args);// member of generic execution\_contexttemplate\<typenameFn\>execution\_context\<void\>operator()(exec\_ontop\_arg\_t,Fn&&fn);// member of execution\_context\< void \>

Effects:

Same as execution_context::operator(). Additionally, function fn is executed in the context of *this (e.g. the stack frame of fn is allocated on stack of *this).

Returns:

The tuple of execution_context and returned arguments passed to the most recent call to execution_context::operator(), if any and a execution_context representing the context that has been suspended .

Note:

The tuple of execution_context and returned arguments from fn are passed as arguments to the context-function of resumed context (if the context is entered the first time) or those arguments are returned from execution_context::operator() within the resumed context.

Note:

Function fn needs to return a tuple of arguments (see description).

Note:

The context calling this function must not be destroyed before the arguments, that will be returned from fn, are preserved at least in the stack frame of the resumed context.

Note:

The returned execution_context indicates if the suspended context has terminated (return from context-function) via bool operator(). If the returned execution_context has terminated no data are transferred in the returned tuple.

Member function operator==()
booloperator==(execution\_contextconst&other)constnoexcept;

Returns:

true if *this and other represent the same execution context, false otherwise.

Throws:

Nothing.

Member function operator!=()
booloperator!=(execution\_contextconst&other)constnoexcept;

Returns:

! (other == * this)

Throws:

Nothing.

Member function operator<()
booloperator\<(execution\_contextconst&other)constnoexcept;

Returns:

true if *this != other is true and the implementation-defined total order of execution_context values places *this before other, false otherwise.

Throws:

Nothing.

Member function operator>()
booloperator\>(execution\_contextconst&other)constnoexcept;

Returns:

other < * this

Throws:

Nothing.

Member function operator<=()
booloperator\<=(execution\_contextconst&other)constnoexcept;

Returns:

! (other < * this)

Throws:

Nothing.

Member function operator>=()
booloperator\>=(execution\_contextconst&other)constnoexcept;

Returns:

! (* this < other)

Throws:

Nothing.

Non-member function operator<<()
template\<typenamecharT,classtraitsT\>std::basic\_ostream\<charT,traitsT\>&operator\<\<(std::basic\_ostream\<charT,traitsT\>&os,execution\_contextconst&other);

Efects:

Writes the representation of other to stream os.

Returns:

os

| | Copyright © 2014 Oliver Kowalke

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

|