[#associators]
== Associators

`cobalt` uses the `associator` concept of asio, but simplifies it.
That is, it has three associators that are member functions of an awaiting promise.

 - `const executor_type & get_executor()` (always `executor`, must return by const ref)
 - `allocator_type get_allocator()` (always `pmr::polymorphic_allocator<void>`)
 - `cancellation_slot_type get_cancellation_slot()` (must have the same IF as `asio::cancellation_slot`)

`cobalt` uses concepts to check if those are present in its `await_suspend` functions.

That way custom coroutines can support cancellation, executors and allocators.

In a custom awaitable you can obtain them like this:

[source,cpp]
----
struct my_awaitable
{
    bool await_ready();
    template<typename T>
    void await_suspend(std::corutine_handle<P> h)
    {
        if constexpr (requires  (Promise p) {p.get_executor();})
            handle_executor(h.promise().get_executor();

        if constexpr (requires (Promise p) {p.get_cancellation_slot();})
            if ((cl = h.promise().get_cancellation_slot()).is_connected())
                cl.emplace<my_cancellation>();
    }

    void await_resume();
};
----

Cancellation gets connected in a `co_await` expression (if supported by the coroutine & awaitable),
including synchronization mechanism like <<race, race>>.

