GCC Code Coverage Report


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