include/boost/capy/ex/io_env.hpp

100.0% Lines (5/5) 100.0% List of functions (2/2)
io_env.hpp
f(x) Functions (2)
Line TLA Hits 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 267x void operator()() const noexcept
44 {
45 267x ex.post(cont);
46 267x }
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 267x post_resume(std::coroutine_handle<> h) const noexcept
122 {
123 267x 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
142