Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_EX_STOP_TOKEN_SUPPORT_HPP
11 : #define BOOST_CAPY_EX_STOP_TOKEN_SUPPORT_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/ex/any_coro.hpp>
15 : #include <boost/capy/ex/get_stop_token.hpp>
16 :
17 : #include <coroutine>
18 : #if BOOST_CAPY_HAS_STOP_TOKEN
19 : #include <stop_token>
20 : #endif
21 : #include <type_traits>
22 :
23 : namespace boost {
24 : namespace capy {
25 :
26 : #if BOOST_CAPY_HAS_STOP_TOKEN
27 :
28 : /** CRTP mixin that adds stop token support to a promise type.
29 :
30 : Inherit from this class to enable two capabilities in your coroutine:
31 :
32 : 1. **Stop token storage** — The mixin stores the `std::stop_token`
33 : that was passed when your coroutine was awaited.
34 :
35 : 2. **Stop token access** — Coroutine code can retrieve the token via
36 : `co_await get_stop_token()`.
37 :
38 : @tparam Derived The derived promise type (CRTP pattern).
39 :
40 : @par Basic Usage
41 :
42 : For coroutines that only need to access their stop token:
43 :
44 : @code
45 : struct my_task
46 : {
47 : struct promise_type : stop_token_support<promise_type>
48 : {
49 : my_task get_return_object();
50 : std::suspend_always initial_suspend() noexcept;
51 : std::suspend_always final_suspend() noexcept;
52 : void return_void();
53 : void unhandled_exception();
54 : };
55 :
56 : // ... awaitable interface ...
57 : };
58 :
59 : my_task example()
60 : {
61 : auto token = co_await get_stop_token();
62 : // Use token...
63 : }
64 : @endcode
65 :
66 : @par Custom Awaitable Transformation
67 :
68 : If your promise needs to transform awaitables (e.g., for affinity or
69 : logging), override `transform_awaitable` instead of `await_transform`:
70 :
71 : @code
72 : struct promise_type : stop_token_support<promise_type>
73 : {
74 : any_executor_ref ex_;
75 :
76 : template<typename A>
77 : auto transform_awaitable(A&& a)
78 : {
79 : // Your custom transformation logic
80 : return make_affine(std::forward<A>(a), ex_);
81 : }
82 : };
83 : @endcode
84 :
85 : The mixin's `await_transform` intercepts @ref get_stop_token_tag and
86 : delegates all other awaitables to your `transform_awaitable`.
87 :
88 : @par Making Your Coroutine Stoppable
89 :
90 : The mixin handles the "inside the coroutine" part—accessing the token.
91 : To receive a token when your coroutine is awaited (satisfying
92 : @ref IoAwaitable), implement the stoppable `await_suspend`
93 : overload on your coroutine return type:
94 :
95 : @code
96 : struct my_task
97 : {
98 : struct promise_type : stop_token_support<promise_type> { ... };
99 :
100 : std::coroutine_handle<promise_type> h_;
101 :
102 : // Stoppable await_suspend receives and stores the token
103 : template<class Ex>
104 : any_coro await_suspend(any_coro cont, Ex const& ex, std::stop_token token)
105 : {
106 : h_.promise().set_stop_token(token); // Store via mixin API
107 : // ... rest of suspend logic ...
108 : }
109 : };
110 : @endcode
111 :
112 : @par Thread Safety
113 : The stop token is stored during `await_suspend` and read during
114 : `co_await get_stop_token()`. These occur on the same logical thread
115 : of execution, so no synchronization is required.
116 :
117 : @see get_stop_token
118 : @see get_stop_token_tag
119 : @see IoAwaitable
120 : */
121 : template<typename Derived>
122 : class stop_token_support
123 : {
124 : std::stop_token stop_token_;
125 :
126 : public:
127 : /** Store a stop token for later retrieval.
128 :
129 : Call this from your coroutine type's stoppable `await_suspend`
130 : overload to make the token available via `co_await get_stop_token()`.
131 :
132 : @param token The stop token to store.
133 : */
134 185 : void set_stop_token(std::stop_token token) noexcept
135 : {
136 185 : stop_token_ = token;
137 185 : }
138 :
139 : /** Return the stored stop token.
140 :
141 : @return The stop token, or a default-constructed token if none was set.
142 : */
143 101 : std::stop_token const& stop_token() const noexcept
144 : {
145 101 : return stop_token_;
146 : }
147 :
148 : /** Transform an awaitable before co_await.
149 :
150 : Override this in your derived promise type to customize how
151 : awaitables are transformed. The default implementation passes
152 : the awaitable through unchanged.
153 :
154 : @param a The awaitable expression from `co_await a`.
155 :
156 : @return The transformed awaitable.
157 : */
158 : template<typename A>
159 : decltype(auto) transform_awaitable(A&& a)
160 : {
161 : return std::forward<A>(a);
162 : }
163 :
164 : /** Intercept co_await expressions.
165 :
166 : This function handles @ref get_stop_token_tag specially, returning
167 : an awaiter that yields the stored stop token. All other awaitables
168 : are delegated to @ref transform_awaitable.
169 :
170 : @param t The awaited expression.
171 :
172 : @return An awaiter for the expression.
173 : */
174 : template<typename T>
175 114 : auto await_transform(T&& t)
176 : {
177 : if constexpr (std::is_same_v<std::decay_t<T>, get_stop_token_tag>)
178 : {
179 : struct awaiter
180 : {
181 : std::stop_token token_;
182 :
183 13 : bool await_ready() const noexcept
184 : {
185 13 : return true;
186 : }
187 :
188 1 : void await_suspend(any_coro) const noexcept
189 : {
190 1 : }
191 :
192 12 : std::stop_token await_resume() const noexcept
193 : {
194 12 : return token_;
195 : }
196 : };
197 14 : return awaiter{stop_token_};
198 : }
199 : else
200 : {
201 25 : return static_cast<Derived*>(this)->transform_awaitable(
202 100 : std::forward<T>(t));
203 : }
204 : }
205 : };
206 :
207 : #endif // BOOST_CAPY_HAS_STOP_TOKEN
208 :
209 : } // namespace capy
210 : } // namespace boost
211 :
212 : #endif
|