GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/ex/stop_token_support.hpp
Date: 2026-01-18 18:26:31
Exec Total Coverage
Lines: 15 15 100.0%
Functions: 22 22 100.0%
Branches: 0 0 -%

Line Branch Exec Source
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 368 void set_stop_token(std::stop_token token) noexcept
135 {
136 368 stop_token_ = token;
137 368 }
138
139 /** Return the stored stop token.
140
141 @return The stop token, or a default-constructed token if none was set.
142 */
143 199 std::stop_token const& stop_token() const noexcept
144 {
145 199 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
213