c++now-affinity.cpp
:
demo scheduler affinity
The example program
c++now-affinity.cpp
uses demo::thread_loop to demonstrate
the behavior of scheduler affinity: the idea is that scheduler
affinity causes the coroutine to resume on the same scheduler as
the one the coroutine was started on. The program implements three
coroutines which have most of their behavior in common:
- Each coroutine is executed from
mainusingsync_wait(fun(loop.get_scheduler())). - Each coroutine prints the id of the thread it is executing on prior to any
co_awaitand after allco_awaitexpression. - Each coroutine
co_awaits the result ofscheduler(sched) | then([]{ ... })where the function passed tothenjust prints the thread id. work2additionally changes the coroutines scheduler to be aninline_schedulerand later restores the original scehduler usingchange_coroutine_scheduler.- While
work1andwork2use the default scheduler (task_schedulera type-erased scheduler which gets initialized from the receiver's environment'sget_scheduler),work3sets the coroutine's scheduler up to beinline_scheduler, effectively causing the coroutine to resume wherever theco_await's expression resumed.
The output of the program is something like the below:
before id=0x1fd635f00
then id =0x16b64b000
after id =0x1fd635f00
before id=0x1fd635f00
then id =0x16b64b000
after1 id=0x16b64b000
then id =0x16b64b000
after2 id=0x1fd635f00
before id=0x1fd635f00
then id =0x16b64b000
after id =0x16b64b000
It shows that:
- The thread on which the
then's function is executed is always the same and different from the thread each of the coroutines started on. - For
work1theco_awaitresumes on the same thread as the one the coroutine was started on. - For
work2the firstco_awaitafterschedule(sched)resumes on the thread used bysched. After restoring the original scheduler theco_awaitresumes on the original thread. - For
work3theco_awaitresumes on the thread used byschedas theinline_schedulerdoesn't do any actual scheduling.
c++now-allocator.cpp
:
demo allocator use
This demo shows how to configure task's environment argument to
use a different allocator than the default std::allocator<std::byte>.
To do so it defines an environment type with_allocator which
defines a nested type alias allocator_type to be
std::pmr::polymorphic_allocator<std::byte>.
The coroutine coro shows how to use read_env to extract the
used allocator object to potentially use it for any allocation
purposes within the coroutine. There are two uses of coro, the
first one using the default which just uses
std::pmr::polymorphic_allocator<std::byte>() to allocate memory.
The second use explicitly specifies the memory resource
std::pmr::new_delete_resource() to initialized the use
std::pmr::polymorphic_allocator<std::byte>.
c++now-basic.cpp
:
demo basic task use
The example
c++now-basic.cpp
shows some basic use of a task:
- The coroutine
basicjustco_awaits the awaiterstd::suspend_never{}which immediately completes. This use demonstrates that any awaiter can beco_awaited by atask<...>. - The coroutine
await_senderdemonstrates the results ofco_awaiting various senders. It uses variations ofjust*to show the different results:co_awaiting a sender completing withset_value_t(), e.g.,just(), produces an expression with typevoid.co_awaiting a sender completing withset_value_t(T), e.g.,just(1), produces an expression with typeT.co_awaiting a sender completing withset_value_t(T0, ..., Tn), e.g.,just(1, true), produces an expression with typetuple<T0, ..., Tn>.co_awaiting a sender completing withset_error_t(E), e.g.,just_error(1), results in an exception of typeEbeing thrown.co_awaiting a sender completing withset_stopped_t(), e.g.,just_stopped(), results in the corouting never getting resumed although all local objects are properly destroyed.
c++now-cancel.cpp
:
demo how a task can actively cancel the work
The example
c++now-cancel.cpp
shows a coroutine co_awaiting just_stopped() which results in the coroutine getting cancelled. The coroutine will
complete with set_stopped().
c++now-errors.cpp
:
demo how to handle errors within task
The example
c++now-errors.cpp
shows examples of how to handle errors within a coroutine:
- The coroutine
error_resultsimplyco_awaits a sender producing an error (just_error(17)). When aco_awaited sender completes withset_error_t(T)an exception of typeTis thrown and the error needs to be handled with atry/catchblock. Otherwise the coroutine itself completes withset_error_t(exception_ptr)where theexception_ptrhold the thrown exception object. - The coroutine
expecteduses a sender algorithmas_expectedwhich is implemented at the top of the example to turn the result of theco_awaited sender into an object of typeexpected<T, E>, avoiding an exception from being thrown.
c++now-query.cpp
:
demo passing a custom environment into a task
The example
c++now-query.cpp
shows how to define and use a custom environment element.
- The coroutine
with_envuses a simple environment namedcontextwhich just defines a custom query forget_valueto obtain a value. The value itself gets initialized from the environment of the receiver used with thetask. - The coroutine
with_fancy_envuses an environment which embed a an object depending the type of the environment of the receiver used with thetask. While the type accessed from within thetaskneeds to be type-erased, the actually stored value can depend on the environment of the upstream receiver.
c++now-result-types.cpp
:
demo the result type of co_await expressions.
The example
c++now-result-types.cpp
shows the result types of successful senders using variatons of just:
co_await just()doesn't produce a value, i.e., the type of the expression isvoid.co_await just(1)produces anint.co_await just(1, true)produces atuple<int, bool>.
c++now-return.cpp
:
shows the different normal values returned
The example
c++now-return.cpp
shows various ways of returning normally (without an error) for a task. Some of the coroutines are set up to produce
specific error results although none of them are actually use:
default_returnshows that the default return type fortask<>isvoid.void_returnexplicitly specifies avoidreturn type.int_returnspecifies the return type asintand returns anintvalue.error_returnspecifies the return type asintand also specifies custom error results.no_error_returnspecifies the return type asintand also specifies that the coroutine can't produce any error.
c++now-stop_token.cpp
:
demo how to get and use a stop token in a `task`
The example
c++now-stop_token.cpp
shows how to get a stop token in side a task and how to use it to cancel active work. It doesn't actually complete
with a set_stopped() but completes with set_value().
- In the coroutine
co_await read_env(get_stop_token)is used to get a stop token. - In the loop the value of
token.stop_requested()is checked to determine if the loop should continue. - In
mainaninplace_stop_sourceis used to have something which can be stopped. - When running the coroutine
stoppingon a separate thread, the environment is changed usingwrite_envto use stop token frommain's stop source in the environment. - After sleeping for a bit,
source.request_stop()is called to trigger cancellation of the coroutine.
c++now-with_error.cpp
:
demo exiting a task with an error
The example
c++now-with_error.cpp
shows how a coroutine can be exited reporting an error without throwing an exception. To do so, the coroutine
uses co_yield with_error(e). By default the task only declares set_error_t(exception_ptr). To return
other errors, an environment declaring a suitable set_error_t(E) completion using the error_types alias is used.
demo::thread_loop is a run_loop whose run() is called from a std::thread.
Technically demo::thread_loop
is a class publicly derived from execution::run_loop which is
also owning a std::thread. The std::thread is constructed with
a function object calling run() on the
demo::thread_loop object.
Destroying the object calls finish() and then join()s the
std::thread: the destructor will block until the execution::run_loop's
run() returns.
The important bit is that work executed on the
demo::thread_loop's scheduler
will be executed on a corresponding std::thread.