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 coroutine handle to an executor.
24 :
25 : Use this as the callback type for `std::stop_callback` instead
26 : of a raw `std::coroutine_handle<>`. Raw handles resume the
27 : 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 : std::coroutine_handle<> h;
40 :
41 : // post() must not throw; stop_callback requires a
42 : // non-throwing invocable.
43 HIT 254 : void operator()() const noexcept
44 : {
45 254 : ex.post(h);
46 254 : }
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. Equivalent to `resume_via_post{executor, h}`.
103 :
104 : @par Example
105 : @code
106 : stop_cb_.emplace(env->stop_token, env->post_resume(h));
107 : @endcode
108 :
109 : @param h The coroutine handle to post on cancellation.
110 :
111 : @return A @ref resume_via_post callable that holds a
112 : non-owning @ref executor_ref and the coroutine handle.
113 : The callable must not outlive the executor it references.
114 :
115 : @see resume_via_post, stop_resume_callback
116 : */
117 : resume_via_post
118 254 : post_resume(std::coroutine_handle<> h) const noexcept
119 : {
120 254 : return resume_via_post{executor, h};
121 : }
122 : };
123 :
124 : /** Type alias for a stop callback that posts through the executor.
125 :
126 : Use this to declare the stop callback member in your awaitable:
127 : @code
128 : std::optional<stop_resume_callback> stop_cb_;
129 : @endcode
130 :
131 : @see resume_via_post, io_env::post_resume
132 : */
133 : using stop_resume_callback = std::stop_callback<resume_via_post>;
134 :
135 : } // capy
136 : } // boost
137 :
138 : #endif
|