TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_IO_ENV_HPP
11 : #define BOOST_CAPY_IO_ENV_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/ex/executor_ref.hpp>
15 :
16 : #include <coroutine>
17 : #include <memory_resource>
18 : #include <stop_token>
19 :
20 : namespace boost {
21 : namespace capy {
22 :
23 : /** Callable that posts a continuation to an executor.
24 :
25 : Use this as the callback type for `std::stop_callback` instead
26 : of resuming a coroutine handle directly. Direct resumption runs
27 : the coroutine inline on whatever thread calls `request_stop()`,
28 : which bypasses the executor and corrupts the thread-local
29 : frame allocator.
30 :
31 : Prefer @ref io_env::post_resume and the @ref stop_resume_callback
32 : alias to construct these—see examples there.
33 :
34 : @see io_env::post_resume, stop_resume_callback
35 : */
36 : struct resume_via_post
37 : {
38 : executor_ref ex;
39 : mutable continuation cont;
40 :
41 : // post() must not throw; stop_callback requires a
42 : // non-throwing invocable.
43 HIT 267 : void operator()() const noexcept
44 : {
45 267 : ex.post(cont);
46 267 : }
47 : };
48 :
49 : /** Execution environment for IoAwaitables.
50 :
51 : This struct bundles the execution context passed through
52 : coroutine chains via the IoAwaitable protocol. It contains
53 : the executor for resumption, a stop token for cancellation,
54 : and an optional frame allocator for coroutine frame allocation.
55 :
56 : @par Lifetime
57 :
58 : Launch functions (@ref run_async, @ref run) own the `io_env` and
59 : guarantee it outlives all tasks and awaitables in the launched
60 : chain. Awaitables receive `io_env const*` in `await_suspend`
61 : and should store it directly, never copy the pointed-to object.
62 :
63 : @par Stop Callback Contract
64 :
65 : Awaitables that register a `std::stop_callback` **must not**
66 : resume the coroutine handle directly. The callback fires
67 : synchronously on the thread that calls `request_stop()`, which
68 : may not be an executor-managed thread. Resuming inline poisons
69 : that thread's TLS frame allocator with the pool's allocator,
70 : causing use-after-free on the next coroutine allocation.
71 :
72 : Use @ref io_env::post_resume and @ref stop_resume_callback:
73 : @code
74 : std::optional<stop_resume_callback> stop_cb_;
75 : // In await_suspend:
76 : stop_cb_.emplace(env->stop_token, env->post_resume(h));
77 : @endcode
78 :
79 : @par Thread Safety
80 : The referenced executor and allocator must remain valid
81 : for the lifetime of any coroutine using this environment.
82 :
83 : @see IoAwaitable, IoRunnable, resume_via_post
84 : */
85 : struct io_env
86 : {
87 : /** The executor for coroutine resumption. */
88 : executor_ref executor;
89 :
90 : /** The stop token for cancellation propagation. */
91 : std::stop_token stop_token;
92 :
93 : /** The frame allocator for coroutine frame allocation.
94 :
95 : When null, the default allocator is used.
96 : */
97 : std::pmr::memory_resource* frame_allocator = nullptr;
98 :
99 : /** Create a resume_via_post callable for this environment.
100 :
101 : Convenience method for registering @ref stop_resume_callback
102 : instances. Wraps the coroutine handle in a @ref continuation
103 : and pairs it with this environment's executor. Equivalent to
104 : `resume_via_post{executor, continuation{h}}`.
105 :
106 : @par Example
107 : @code
108 : stop_cb_.emplace(env->stop_token, env->post_resume(h));
109 : @endcode
110 :
111 : @param h The coroutine handle to wrap in a continuation
112 : and post on cancellation.
113 :
114 : @return A @ref resume_via_post callable that holds a
115 : non-owning @ref executor_ref and a @ref continuation.
116 : The callable must not outlive the executor it references.
117 :
118 : @see resume_via_post, stop_resume_callback
119 : */
120 : resume_via_post
121 267 : post_resume(std::coroutine_handle<> h) const noexcept
122 : {
123 267 : return resume_via_post{executor, continuation{h}};
124 : }
125 : };
126 :
127 : /** Type alias for a stop callback that posts through the executor.
128 :
129 : Use this to declare the stop callback member in your awaitable:
130 : @code
131 : std::optional<stop_resume_callback> stop_cb_;
132 : @endcode
133 :
134 : @see resume_via_post, io_env::post_resume
135 : */
136 : using stop_resume_callback = std::stop_callback<resume_via_post>;
137 :
138 : } // capy
139 : } // boost
140 :
141 : #endif
|