Skip to content

4. Quick reference guide

Alexander Damian edited this page Aug 21, 2019 · 8 revisions

V1 and V2 coroutine API

Differences between the two API formats.

Head-to-head comparison of features.

Feature V1 V2 Notes
Decouples returning value with coroutine termination Yes No
Computed value Set via promise or context Returned directly
Flexibility Higher Lower
Supports buffered futures Yes No In V2 api similar behavior has be be created out-of-band
Convert a regular function to a coroutine Harder Easier Async IO tasks require no modification in V2 api!
Support exceptions Yes Yes
Can nest coroutines or IO tasks Yes Yes

Function signatures (V1)

The following table shows the main coroutine and async IO task V1 signatures that are required and the context in which they are executed. These functions can represent any callable object such as lambdas or regular functions.

Coroutine task AsyncIo task
int (CoroContextPtr<T>, Args&&...) int (ThreadPromisePtr<T>, Args&&...)
// Posting a V1 coroutine 
ThreadFuturePtr<T> tctx = dispatcher.postAsyncIo([](CoroContextPtr<T> ctx)->int{
    //do stuff...
    ctx->set(T{}); //return future. main task awakes here
    //do stuff... 
    return 0;
});

// Compact version
ThreadFuturePtr<T> tctx = dispatcher.postAsyncIo([](CoroContextPtr<T> ctx)->int{
    //do stuff...
    return ctx->set(T{}); //main task awakes here
});
// Posting a V1 async IO 
ThreadFuturePtr<T> tctx = dispatcher.postAsyncIo([](ThreadPromisePtr<T> promise)->int{
    //do stuff...
    promise->set(T{}); //return future. main task awakes here
    //do stuff... 
    return 0;
});

// Compact version
ThreadFuturePtr<T> tctx = dispatcher.postAsyncIo([](ThreadPromisePtr<T> promise)->int{
    //do stuff...
    return promise->set(T{}); //return future. main task awakes here
});

Function signatures (V2)

The following table shows the main coroutine and async IO task signatures that are required if using the simpler V2 API. Note that VoidContextPtr is a class similar to CoroContextPtr<T> except that it does not support setting any values since values are returned directly on the return line. Also it is not templated on any type.

Coroutine task AsyncIo task
T (VoidContextPtr, Args&&...) T (Args&&...)
// Posting a V2 coroutine 
ThreadContextPtr<T> tctx = dispatcher.post2([](VoidContextPtr ctx)->T {
    //do stuff... 
    return T{}; //return future. main task awakes here
});
// Posting a V2 async IO 
ThreadFuturePtr<T> tctx = dispatcher.postAsyncIo2([]()->T {
    //do stuff... 
    return T{}; //return future. main task awakes here
});

Note: V2 api dispatcher functions have the same name as V1 counterparts but with a 2 appended (post2(), postAyncIo2() etc.)

Calling conventions and return types

Main below refers simply to your application's main thread or any other thread outside of the quantum framework.

Method Instance Location Return Type
post()/post2() Dispatcher main ThreadContextPtr<T>
post()/post2() CoroContextPtr<T>/VoidContextPtr coroutine CoroContextPtr<T>
postAsyncIo()/postAsyncIo2() Dispatcher main ThreadFuturePtr<T>
postAsyncIo()/postAsyncIo2() CoroContextPtr<T>/VoidContextPtr coroutine CoroFuturePtr<T>
postFirst()/postFirst2() Dispatcher main ThreadContextPtr<T>
postFirst()/postFirst2() CoroContextPtr<T>/VoidContextPtr coroutine CoroContextPtr<T>
then()/then2() ThreadContextPtr<T> main ThreadContextPtr<T>
then()/then2() CoroContextPtr<T>/VoidContextPtr coroutine CoroContextPtr<T>
onError()/onError2() ThreadContextPtr<T> main ThreadContextPtr<T>
onError()/onError2() CoroContextPtr<T>/VoidContextPtr coroutine CoroContextPtr<T>
finally()/finally2() ThreadContextPtr<T> main ThreadContextPtr<T>
finally()/finally2() CoroContextPtr<T>/VoidContextPtr coroutine CoroContextPtr<T>
end() ThreadContextPtr<T> main ThreadContextPtr<T>
end() CoroContextPtr<T>/VoidContextPtr coroutine CoroContextPtr<T>

Note that objects in quantum are named using the location in which they are supposed to be used for disambiguation. As such CoroContextPtr<T> can only appear in a coroutine, and ThreadPromise<T> can only appear in a regular thread (async IO task).

It is also perfectly possible to combine V1 and V2 apis:

auto ctx = dispatcher.postFirst(...)->then2(...)->then(...)->finally2(...)->end();

Blocking and yielding functions

The following functions block or yield implicitly with the exception of yield() which yields explicitly. Note that ICoroSync is a base class of CoroContext<T> and as such the derived class can be passed directly into the function.

Method Instance Location Action Requires CoroSyncPtr as argument
yield() CoroContextPtr<T>/VoidContextPtr coroutine yields yes
getXXX() ThreadContextPtr<T> main blocks no
getXXX() CoroContextPtr<T>/VoidContextPtr coroutine yields yes
set() ThreadPromisePtr<T> IO thread blocks(1) no
set() CoroContextPtr<T>/VoidContextPtr coroutine yields(1) no
push() ThreadPromisePtr<T> IO thread blocks(1)(2) no
push() CoroContextPtr<T>/VoidContextPtr coroutine blocks(1)(2) no
pull() ThreadContextPtr<T> main blocks(2) no
pull() CoroContextPtr<T>/VoidContextPtr coroutine yields(2) yes
waitXXX() ThreadContextPtr<T> main blocks no
waitXXX() CoroContextPtr<T>/VoidContextPtr coroutine yields yes
lock() Mutex main blocks no
lock() Mutex coroutine yields yes
waitXXX() ConditionVariable main blocks no
waitXXX() ConditionVariable coroutine yields yes

(1) May block for a very small period of time if there is contention on the shared promise/future state. (2) Only applicable to buffered futures.

Using 'auto'

Note that auto can drastically simplify your code and reduce typing:

// Posting a V1 async IO 
ThreadFuturePtr<T> tctx = dispatcher.postAsyncIo([](ThreadPromisePtr<T> promise)->int{
    return promise->set(42);
});

Now becomes...

// Posting a V1 async IO 
auto tctx = dispatcher.postAsyncIo([](auto promise)->int{
    return promise->set(42);
});