== Executors

Since everything is asynchronous the library needs to use an event-loop.
Because everything is single-threaded, it can be assumed that there is exactly one executor
per thread, which will suffice for 97% of use-cases.
Therefore, there is a `thread_local` executor that gets used as default
by the coroutine objects (although stored by copy in the coroutine promise).

Likewise, there is one `executor` type used by the library,
which defaults to `asio::any_io_executor`.

NOTE: If you write your own coroutine, it should hold a copy of the executor,
and have a `get_executor` function returning it by const reference.

=== Using Strands

While strands can be used, they are not compatible with the `thread_local` executor.
This is because they might switch threads, thus they can't be `thread_local`.

If you wish to use strands (e.g. through a <<spawn, spawn>>)
the executor for any <<promise, promise>>, <<generator, generator>> or <<channel, channel>>
must be assigned manually.

In the case of a <<channel, channel>> this is a constructor argument,
but for the other coroutine types, `asio::executor_arg` needs to be used.
This is done by having `asio::executor_arg_t` (somewhere) in the argument
list directly followed by the executor to be used in the argument list of the coroutine, e.g.:

[source,cpp]
----
cobalt::promise<void> example_with_executor(int some_arg, asio::executor_arg_t, cobalt::executor);
----

This way the coroutine-promise can pick up the executor from the third argument,
instead of defaulting to the thread_local one.

The arguments can of course be defaulted, to make them less inconvenient,
if they are sometimes with a thread_local executor.

[source,cpp]
----
cobalt::promise<void> example_with_executor(int some_arg,
                                           asio::executor_arg_t = asio::executor_arg,
                                           cobalt::executor = cobalt::this_thread::get_executor());
----

If this gets omitted on a strand an exception of type `asio::bad_allocator` is thrown,
or - worse - the wrong executor is used.

== polymorphic memory resource

Similarly, the library uses a thread_local `pmr::memory_resource` to allocate
coroutine frames & to use as allocator on asynchronous operations.



The reason is, that users may want to customize allocations,
e.g. to avoid locks, limit memory usage or monitor usage.
`pmr` allows us to achieve this without introducing unnecessary template parameters,
i.e. no `promise<T, Allocator>` complexity.
Using `pmr` however does introduce some minimal overheads,
so a user has the option to disable by defining `BOOST_COBALT_NO_PMR`.

<<op, op>> uses an internal resource optimized for asio's allocator usages
and <<gather, gather>>, <<race,race>> and <<join,join>> use a monotonic resource to miminize allocations.
Both still work with `BOOST_COBALT_NO_PMR` defined, in which case they'll use `new/delete` as upstream allocations.

<<main,main>> and <<thread,thread>> single `pmr::unsynchronized_pool_resource` per thread with PMR enabled.

NOTE: If you write your own coroutine, it should have a get_allocator function
returning a `pmr::polymorphic_allocator<void>`.

== cancellation

cobalt uses implicit cancellation based on `asio::cancellation_signal`.
This is mostly used implicitly (e.g. with <<race, race>>),
so that there is very little explicit use in the examples.

NOTE: If you write custom coroutine it must return a `cancellation_slot` from a
`get_cancellation_slot` function in order to be able to cancel other operations.

NOTE: If you write a custom awaitable, it can use that function in await_suspend to receive cancellation signals.

