1// <stop_token> -*- C++ -*- 
2 
3// Copyright (C) 2019-2021 Free Software Foundation, Inc. 
4// 
5// This file is part of the GNU ISO C++ Library. This library is free 
6// software; you can redistribute it and/or modify it under the 
7// terms of the GNU General Public License as published by the 
8// Free Software Foundation; either version 3, or (at your option) 
9// any later version. 
10 
11// This library is distributed in the hope that it will be useful, 
12// but WITHOUT ANY WARRANTY; without even the implied warranty of 
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
14// GNU General Public License for more details. 
15 
16// Under Section 7 of GPL version 3, you are granted additional 
17// permissions described in the GCC Runtime Library Exception, version 
18// 3.1, as published by the Free Software Foundation. 
19 
20// You should have received a copy of the GNU General Public License and 
21// a copy of the GCC Runtime Library Exception along with this program; 
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 
23// <http://www.gnu.org/licenses/>. 
24 
25/** @file include/stop_token 
26 * This is a Standard C++ Library header. 
27 */ 
28 
29#ifndef _GLIBCXX_STOP_TOKEN 
30#define _GLIBCXX_STOP_TOKEN 
31 
32#if __cplusplus > 201703L 
33 
34#include <atomic> 
35#include <bits/std_thread.h> 
36 
37#include <semaphore> 
38 
39#define __cpp_lib_jthread 201911L 
40 
41namespace std _GLIBCXX_VISIBILITY(default
42
43_GLIBCXX_BEGIN_NAMESPACE_VERSION 
44 
45 /// Tag type indicating a stop_source should have no shared-stop-state. 
46 struct nostopstate_t { explicit nostopstate_t() = default; }; 
47 inline constexpr nostopstate_t nostopstate{}; 
48 
49 class stop_source
50 
51 /// Allow testing whether a stop request has been made on a `stop_source`. 
52 class stop_token 
53
54 public
55 stop_token() noexcept = default
56 
57 stop_token(const stop_token&) noexcept = default
58 stop_token(stop_token&&) noexcept = default
59 
60 ~stop_token() = default
61 
62 stop_token
63 operator=(const stop_token&) noexcept = default
64 
65 stop_token
66 operator=(stop_token&&) noexcept = default
67 
68 [[nodiscard]] 
69 bool 
70 stop_possible() const noexcept 
71
72 return static_cast<bool>(_M_state) && _M_state->_M_stop_possible(); 
73
74 
75 [[nodiscard]] 
76 bool 
77 stop_requested() const noexcept 
78
79 return static_cast<bool>(_M_state) && _M_state->_M_stop_requested(); 
80
81 
82 void 
83 swap(stop_token& __rhs) noexcept 
84 { _M_state.swap(other&: __rhs._M_state); } 
85 
86 [[nodiscard]] 
87 friend bool 
88 operator==(const stop_token& __a, const stop_token& __b
89 { return __a._M_state == __b._M_state; } 
90 
91 friend void 
92 swap(stop_token& __lhs, stop_token& __rhs) noexcept 
93 { __lhs.swap(__rhs); } 
94 
95 private
96 friend class stop_source
97 template<typename _Callback> 
98 friend class stop_callback
99 
100 static void 
101 _S_yield() noexcept 
102
103#if defined __i386__ || defined __x86_64__ 
104 __builtin_ia32_pause(); 
105#endif 
106 this_thread::yield(); 
107
108 
109#ifndef __cpp_lib_semaphore 
110 struct binary_semaphore 
111
112 explicit binary_semaphore(int __d) : _M_counter(__d > 0) { } 
113 
114 void release() { _M_counter.fetch_add(1, memory_order::release); } 
115 
116 void acquire() 
117
118 int __old = 1
119 while (!_M_counter.compare_exchange_weak(__old, 0
120 memory_order::acquire, 
121 memory_order::relaxed)) 
122
123 __old = 1
124 _S_yield(); 
125
126
127 
128 atomic<int> _M_counter; 
129 }; 
130#endif 
131 
132 struct _Stop_cb 
133
134 using __cb_type = void(_Stop_cb*) noexcept
135 __cb_type* _M_callback
136 _Stop_cb* _M_prev = nullptr
137 _Stop_cb* _M_next = nullptr
138 bool* _M_destroyed = nullptr
139 binary_semaphore _M_done{0}; 
140 
141 [[__gnu__::__nonnull__]] 
142 explicit 
143 _Stop_cb(__cb_type* __cb
144 : _M_callback(__cb
145 { } 
146 
147 void _M_run() noexcept { _M_callback(this); } 
148 }; 
149 
150 struct _Stop_state_t 
151
152 using value_type = uint32_t; 
153 static constexpr value_type _S_stop_requested_bit = 1
154 static constexpr value_type _S_locked_bit = 2
155 static constexpr value_type _S_ssrc_counter_inc = 4
156 
157 std::atomic<value_type> _M_owners{1}; 
158 std::atomic<value_type> _M_value{_S_ssrc_counter_inc}; 
159 _Stop_cb* _M_head = nullptr
160 std::thread::id _M_requester
161 
162 _Stop_state_t() = default
163 
164 bool 
165 _M_stop_possible() noexcept 
166
167 // true if a stop request has already been made or there are still 
168 // stop_source objects that would allow one to be made. 
169 return _M_value.load(m: memory_order::acquire) & ~_S_locked_bit
170
171 
172 bool 
173 _M_stop_requested() noexcept 
174
175 return _M_value.load(m: memory_order::acquire) & _S_stop_requested_bit
176
177 
178 void 
179 _M_add_owner() noexcept 
180
181 _M_owners.fetch_add(i: 1, m: memory_order::relaxed); 
182
183 
184 void 
185 _M_release_ownership() noexcept 
186
187 if (_M_owners.fetch_sub(i: 1, m: memory_order::acq_rel) == 1
188 delete this
189
190 
191 void 
192 _M_add_ssrc() noexcept 
193
194 _M_value.fetch_add(i: _S_ssrc_counter_inc, m: memory_order::relaxed); 
195
196 
197 void 
198 _M_sub_ssrc() noexcept 
199
200 _M_value.fetch_sub(i: _S_ssrc_counter_inc, m: memory_order::release); 
201
202 
203 // Obtain lock. 
204 void 
205 _M_lock() noexcept 
206
207 // Can use relaxed loads to get the current value. 
208 // The successful call to _M_try_lock is an acquire operation. 
209 auto __old = _M_value.load(m: memory_order::relaxed); 
210 while (!_M_try_lock(curval&: __old, failure: memory_order::relaxed)) 
211 { } 
212
213 
214 // Precondition: calling thread holds the lock. 
215 void 
216 _M_unlock() noexcept 
217
218 _M_value.fetch_sub(i: _S_locked_bit, m: memory_order::release); 
219
220 
221 bool 
222 _M_request_stop() noexcept 
223
224 // obtain lock and set stop_requested bit 
225 auto __old = _M_value.load(m: memory_order::acquire); 
226 do 
227
228 if (__old & _S_stop_requested_bit) // stop request already made 
229 return false
230
231 while (!_M_try_lock_and_stop(curval&: __old)); 
232 
233 _M_requester = this_thread::get_id(); 
234 
235 while (_M_head
236
237 bool __last_cb
238 _Stop_cb* __cb = _M_head
239 _M_head = _M_head->_M_next
240 if (_M_head
241
242 _M_head->_M_prev = nullptr
243 __last_cb = false
244
245 else 
246 __last_cb = true
247 
248 // Allow other callbacks to be unregistered while __cb runs. 
249 _M_unlock(); 
250 
251 bool __destroyed = false
252 __cb->_M_destroyed = &__destroyed
253 
254 // run callback 
255 __cb->_M_run(); 
256 
257 if (!__destroyed
258
259 __cb->_M_destroyed = nullptr
260 
261 // synchronize with destructor of stop_callback that owns *__cb 
262 if (!__gnu_cxx::__is_single_threaded()) 
263 __cb->_M_done.release(); 
264
265 
266 // Avoid relocking if we already know there are no more callbacks. 
267 if (__last_cb
268 return true
269 
270 _M_lock(); 
271
272 
273 _M_unlock(); 
274 return true
275
276 
277 [[__gnu__::__nonnull__]] 
278 bool 
279 _M_register_callback(_Stop_cb* __cb) noexcept 
280
281 auto __old = _M_value.load(m: memory_order::acquire); 
282 do 
283
284 if (__old & _S_stop_requested_bit) // stop request already made 
285
286 __cb->_M_run(); // run synchronously 
287 return false
288
289 
290 if (__old < _S_ssrc_counter_inc) // no stop_source owns *this 
291 // No need to register callback if no stop request can be made. 
292 // Returning false also means the stop_callback does not share 
293 // ownership of this state, but that's not observable. 
294 return false
295
296 while (!_M_try_lock(curval&: __old)); 
297 
298 __cb->_M_next = _M_head
299 if (_M_head
300
301 _M_head->_M_prev = __cb
302
303 _M_head = __cb
304 _M_unlock(); 
305 return true
306
307 
308 // Called by ~stop_callback just before destroying *__cb. 
309 [[__gnu__::__nonnull__]] 
310 void 
311 _M_remove_callback(_Stop_cb* __cb
312
313 _M_lock(); 
314 
315 if (__cb == _M_head
316
317 _M_head = _M_head->_M_next
318 if (_M_head
319 _M_head->_M_prev = nullptr
320 _M_unlock(); 
321 return
322
323 else if (__cb->_M_prev
324
325 __cb->_M_prev->_M_next = __cb->_M_next
326 if (__cb->_M_next
327 __cb->_M_next->_M_prev = __cb->_M_prev
328 _M_unlock(); 
329 return
330
331 
332 _M_unlock(); 
333 
334 // Callback is not in the list, so must have been removed by a call to 
335 // _M_request_stop. 
336 
337 // Despite appearances there is no data race on _M_requester. The only 
338 // write to it happens before the callback is removed from the list, 
339 // and removing it from the list happens before this read. 
340 if (!(_M_requester == this_thread::get_id())) 
341
342 // Synchronize with completion of callback. 
343 __cb->_M_done.acquire(); 
344 // Safe for ~stop_callback to destroy *__cb now. 
345 return
346
347 
348 if (__cb->_M_destroyed
349 *__cb->_M_destroyed = true
350
351 
352 // Try to obtain the lock. 
353 // Returns true if the lock is acquired (with memory order acquire). 
354 // Otherwise, sets __curval = _M_value.load(__failure) and returns false. 
355 // Might fail spuriously, so must be called in a loop. 
356 bool 
357 _M_try_lock(value_type& __curval
358 memory_order __failure = memory_order::acquire) noexcept 
359
360 return _M_do_try_lock(__curval, newbits: 0, success: memory_order::acquire, __failure); 
361
362 
363 // Try to obtain the lock to make a stop request. 
364 // Returns true if the lock is acquired and the _S_stop_requested_bit is 
365 // set (with memory order acq_rel so that other threads see the request). 
366 // Otherwise, sets __curval = _M_value.load(memory_order::acquire) and 
367 // returns false. 
368 // Might fail spuriously, so must be called in a loop. 
369 bool 
370 _M_try_lock_and_stop(value_type& __curval) noexcept 
371
372 return _M_do_try_lock(__curval, newbits: _S_stop_requested_bit
373 success: memory_order::acq_rel, failure: memory_order::acquire); 
374
375 
376 bool 
377 _M_do_try_lock(value_type& __curval, value_type __newbits
378 memory_order __success, memory_order __failure) noexcept 
379
380 if (__curval & _S_locked_bit
381
382 _S_yield(); 
383 __curval = _M_value.load(m: __failure); 
384 return false
385
386 __newbits |= _S_locked_bit
387 return _M_value.compare_exchange_weak(i1&: __curval, i2: __curval | __newbits
388 m1: __success, m2: __failure); 
389
390 }; 
391 
392 struct _Stop_state_ref 
393
394 _Stop_state_ref() = default
395 
396 explicit 
397 _Stop_state_ref(const stop_source&) 
398 : _M_ptr(new _Stop_state_t()) 
399 { } 
400 
401 _Stop_state_ref(const _Stop_state_ref& __other) noexcept 
402 : _M_ptr(__other._M_ptr
403
404 if (_M_ptr
405 _M_ptr->_M_add_owner(); 
406
407 
408 _Stop_state_ref(_Stop_state_ref&& __other) noexcept 
409 : _M_ptr(__other._M_ptr
410
411 __other._M_ptr = nullptr
412
413 
414 _Stop_state_ref
415 operator=(const _Stop_state_ref& __other) noexcept 
416
417 if (auto __ptr = __other._M_ptr; __ptr != _M_ptr
418
419 if (__ptr
420 __ptr->_M_add_owner(); 
421 if (_M_ptr
422 _M_ptr->_M_release_ownership(); 
423 _M_ptr = __ptr
424
425 return *this
426
427 
428 _Stop_state_ref
429 operator=(_Stop_state_ref&& __other) noexcept 
430
431 _Stop_state_ref(std::move(__other)).swap(other&: *this); 
432 return *this
433
434 
435 ~_Stop_state_ref() 
436
437 if (_M_ptr
438 _M_ptr->_M_release_ownership(); 
439
440 
441 void 
442 swap(_Stop_state_ref& __other) noexcept 
443 { std::swap(a&: _M_ptr, b&: __other._M_ptr); } 
444 
445 explicit operator bool() const noexcept { return _M_ptr != nullptr; } 
446 
447 _Stop_state_t* operator->() const noexcept { return _M_ptr; } 
448 
449#if __cpp_impl_three_way_comparison >= 201907L 
450 friend bool 
451 operator==(const _Stop_state_ref&, const _Stop_state_ref&) = default
452#else 
453 friend bool 
454 operator==(const _Stop_state_ref& __lhs, const _Stop_state_ref& __rhs) 
455 noexcept 
456 { return __lhs._M_ptr == __rhs._M_ptr; } 
457 
458 friend bool 
459 operator!=(const _Stop_state_ref& __lhs, const _Stop_state_ref& __rhs) 
460 noexcept 
461 { return __lhs._M_ptr != __rhs._M_ptr; } 
462#endif 
463 
464 private
465 _Stop_state_t* _M_ptr = nullptr
466 }; 
467 
468 _Stop_state_ref _M_state
469 
470 explicit 
471 stop_token(const _Stop_state_ref& __state) noexcept 
472 : _M_state{__state
473 { } 
474 }; 
475 
476 /// A type that allows a stop request to be made. 
477 class stop_source 
478
479 public
480 stop_source() : _M_state(*this
481 { } 
482 
483 explicit stop_source(std::nostopstate_t) noexcept 
484 { } 
485 
486 stop_source(const stop_source& __other) noexcept 
487 : _M_state(__other._M_state
488
489 if (_M_state
490 _M_state->_M_add_ssrc(); 
491
492 
493 stop_source(stop_source&&) noexcept = default
494 
495 stop_source
496 operator=(const stop_source& __other) noexcept 
497
498 if (_M_state != __other._M_state
499
500 stop_source __sink(std::move(*this)); 
501 _M_state = __other._M_state
502 if (_M_state
503 _M_state->_M_add_ssrc(); 
504
505 return *this
506
507 
508 stop_source
509 operator=(stop_source&&) noexcept = default
510 
511 ~stop_source() 
512
513 if (_M_state
514 _M_state->_M_sub_ssrc(); 
515
516 
517 [[nodiscard]] 
518 bool 
519 stop_possible() const noexcept 
520
521 return static_cast<bool>(_M_state); 
522
523 
524 [[nodiscard]] 
525 bool 
526 stop_requested() const noexcept 
527
528 return static_cast<bool>(_M_state) && _M_state->_M_stop_requested(); 
529
530 
531 bool 
532 request_stop() const noexcept 
533
534 if (stop_possible()) 
535 return _M_state->_M_request_stop(); 
536 return false
537
538 
539 [[nodiscard]] 
540 stop_token 
541 get_token() const noexcept 
542
543 return stop_token{_M_state}; 
544
545 
546 void 
547 swap(stop_source& __other) noexcept 
548
549 _M_state.swap(other&: __other._M_state); 
550
551 
552 [[nodiscard]] 
553 friend bool 
554 operator==(const stop_source& __a, const stop_source& __b) noexcept 
555
556 return __a._M_state == __b._M_state
557
558 
559 friend void 
560 swap(stop_source& __lhs, stop_source& __rhs) noexcept 
561
562 __lhs.swap(other&: __rhs); 
563
564 
565 private
566 stop_token::_Stop_state_ref _M_state
567 }; 
568 
569 /// A wrapper for callbacks to be run when a stop request is made. 
570 template<typename _Callback> 
571 class [[nodiscard]] stop_callback 
572
573 static_assert(is_nothrow_destructible_v<_Callback>); 
574 static_assert(is_invocable_v<_Callback>); 
575 
576 public
577 using callback_type = _Callback; 
578 
579 template<typename _Cb, 
580 enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0
581 explicit 
582 stop_callback(const stop_token& __token, _Cb&& __cb
583 noexcept(is_nothrow_constructible_v<_Callback, _Cb>) 
584 : _M_cb(std::forward<_Cb>(__cb)) 
585
586 if (auto __state = __token._M_state
587
588 if (__state->_M_register_callback(cb: &_M_cb)) 
589 _M_state.swap(other&: __state); 
590
591
592 
593 template<typename _Cb, 
594 enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0
595 explicit 
596 stop_callback(stop_token&& __token, _Cb&& __cb
597 noexcept(is_nothrow_constructible_v<_Callback, _Cb>) 
598 : _M_cb(std::forward<_Cb>(__cb)) 
599
600 if (auto& __state = __token._M_state
601
602 if (__state->_M_register_callback(cb: &_M_cb)) 
603 _M_state.swap(other&: __state); 
604
605
606 
607 ~stop_callback() 
608
609 if (_M_state
610
611 _M_state->_M_remove_callback(cb: &_M_cb); 
612
613
614 
615 stop_callback(const stop_callback&) = delete
616 stop_callback& operator=(const stop_callback&) = delete
617 stop_callback(stop_callback&&) = delete
618 stop_callback& operator=(stop_callback&&) = delete
619 
620 private
621 struct _Cb_impl : stop_token::_Stop_cb 
622
623 template<typename _Cb> 
624 explicit 
625 _Cb_impl(_Cb&& __cb
626 : _Stop_cb(&_S_execute), 
627 _M_cb(std::forward<_Cb>(__cb)) 
628 { } 
629 
630 _Callback _M_cb
631 
632 [[__gnu__::__nonnull__]] 
633 static void 
634 _S_execute(_Stop_cb* __that) noexcept 
635
636 _Callback& __cb = static_cast<_Cb_impl*>(__that)->_M_cb; 
637 std::forward<_Callback>(__cb)(); 
638
639 }; 
640 
641 _Cb_impl _M_cb
642 stop_token::_Stop_state_ref _M_state
643 }; 
644 
645 template<typename _Callback> 
646 stop_callback(stop_token, _Callback) -> stop_callback<_Callback>; 
647 
648_GLIBCXX_END_NAMESPACE_VERSION 
649} // namespace 
650#endif // __cplusplus > 201703L 
651#endif // _GLIBCXX_STOP_TOKEN 
652