libstdc++
stop_token
Go to the documentation of this file.
1 // <stop_token> -*- C++ -*-
2 
3 // Copyright (C) 2019 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_mutex.h>
36 #include <ext/concurrence.h>
37 #include <bits/unique_ptr.h>
38 #include <bits/shared_ptr.h>
39 
40 #ifdef _GLIBCXX_HAS_GTHREADS
41 # define __cpp_lib_jthread 201907L
42 #endif
43 
44 namespace std _GLIBCXX_VISIBILITY(default)
45 {
46 _GLIBCXX_BEGIN_NAMESPACE_VERSION
47 
48  /// Tag type indicating a stop_source should have no shared-stop-state.
49  struct nostopstate_t { explicit nostopstate_t() = default; };
50  inline constexpr nostopstate_t nostopstate{};
51 
52  /// Allow testing whether a stop request has been made on a `stop_source`.
53  class stop_token
54  {
55  public:
56  stop_token() noexcept = default;
57 
58  stop_token(const stop_token& __other) noexcept = default;
59  stop_token(stop_token&& __other) noexcept = default;
60 
61  ~stop_token() = default;
62 
63  stop_token&
64  operator=(const stop_token& __rhs) noexcept = default;
65 
66  stop_token&
67  operator=(stop_token&& __rhs) noexcept = default;
68 
69  [[nodiscard]]
70  bool
71  stop_possible() const noexcept
72  {
73  return static_cast<bool>(_M_state);
74  }
75 
76  [[nodiscard]]
77  bool
78  stop_requested() const noexcept
79  {
80  return stop_possible() && _M_state->_M_stop_requested();
81  }
82 
83  void
84  swap(stop_token& __rhs) noexcept
85  { _M_state.swap(__rhs._M_state); }
86 
87  [[nodiscard]]
88  friend bool
89  operator==(const stop_token& __a, const stop_token& __b)
90  {
91  return __a._M_state == __b._M_state;
92  }
93 
94  [[nodiscard]]
95  friend bool
96  operator!=(const stop_token& __a, const stop_token& __b)
97  {
98  return __a._M_state != __b._M_state;
99  }
100 
101  friend void
102  swap(stop_token& __lhs, stop_token& __rhs) noexcept
103  { __lhs.swap(__rhs); }
104 
105  private:
106  friend class stop_source;
107  template<typename _Callback>
108  friend class stop_callback;
109 
110  struct _Stop_cb
111  {
112  void(*_M_callback)(_Stop_cb*);
113  _Stop_cb* _M_prev = nullptr;
114  _Stop_cb* _M_next = nullptr;
115 
116  template<typename _Cb>
117  _Stop_cb(_Cb&& __cb)
118  : _M_callback(std::forward<_Cb>(__cb))
119  { }
120 
121  bool
122  _M_linked() const noexcept
123  {
124  return (_M_prev != nullptr)
125  || (_M_next != nullptr);
126  }
127 
128  static void
129  _S_execute(_Stop_cb* __cb) noexcept
130  {
131  __cb->_M_callback(__cb);
132  __cb->_M_prev = __cb->_M_next = nullptr;
133  }
134  };
135 
136  struct _Stop_state_t
137  {
138  std::atomic<bool> _M_stopped{false};
139  _Stop_cb* _M_head = nullptr;
140 #ifdef _GLIBCXX_HAS_GTHREADS
141  std::mutex _M_mtx;
142 #endif
143 
144  _Stop_state_t() = default;
145 
146  bool
147  _M_stop_requested() noexcept
148  {
149  return _M_stopped;
150  }
151 
152  bool
153  _M_request_stop()
154  {
155  bool __stopped = false;
156  if (_M_stopped.compare_exchange_strong(__stopped, true))
157  {
158 #ifdef _GLIBCXX_HAS_GTHREADS
159  std::lock_guard<std::mutex> __lck{_M_mtx};
160 #endif
161  while (_M_head)
162  {
163  auto __p = _M_head;
164  _M_head = _M_head->_M_next;
165  _Stop_cb::_S_execute(__p);
166  }
167  return true;
168  }
169  return false;
170  }
171 
172  bool
173  _M_register_callback(_Stop_cb* __cb)
174  {
175 #ifdef _GLIBCXX_HAS_GTHREADS
176  std::lock_guard<std::mutex> __lck{_M_mtx};
177 #endif
178  if (_M_stopped)
179  return false;
180 
181  __cb->_M_next = _M_head;
182  if (_M_head)
183  {
184  _M_head->_M_prev = __cb;
185  }
186  _M_head = __cb;
187  return true;
188  }
189 
190  void
191  _M_remove_callback(_Stop_cb* __cb)
192  {
193 #ifdef _GLIBCXX_HAS_GTHREADS
194  std::lock_guard<std::mutex> __lck{_M_mtx};
195 #endif
196  if (__cb == _M_head)
197  {
198  _M_head = _M_head->_M_next;
199  if (_M_head)
200  {
201  _M_head->_M_prev = nullptr;
202  }
203  }
204  else if (!__cb->_M_linked())
205  {
206  return;
207  }
208  else
209  {
210  __cb->_M_prev->_M_next = __cb->_M_next;
211  if (__cb->_M_next)
212  {
213  __cb->_M_next->_M_prev = __cb->_M_prev;
214  }
215  }
216  }
217  };
218 
219  using _Stop_state = std::shared_ptr<_Stop_state_t>;
220  _Stop_state _M_state;
221 
222  explicit
223  stop_token(const _Stop_state& __state) noexcept
224  : _M_state{__state}
225  { }
226  };
227 
228  /// A type that allows a stop request to be made.
229  class stop_source
230  {
231  public:
232  stop_source()
233  : _M_state(std::make_shared<stop_token::_Stop_state_t>())
234  { }
235 
236  explicit stop_source(std::nostopstate_t) noexcept
237  { }
238 
239  stop_source(const stop_source& __other) noexcept
240  : _M_state(__other._M_state)
241  { }
242 
243  stop_source(stop_source&& __other) noexcept
244  : _M_state(std::move(__other._M_state))
245  { }
246 
247  stop_source&
248  operator=(const stop_source& __rhs) noexcept
249  {
250  if (_M_state != __rhs._M_state)
251  _M_state = __rhs._M_state;
252  return *this;
253  }
254 
255  stop_source&
256  operator=(stop_source&& __rhs) noexcept
257  {
258  std::swap(_M_state, __rhs._M_state);
259  return *this;
260  }
261 
262  [[nodiscard]]
263  bool
264  stop_possible() const noexcept
265  {
266  return static_cast<bool>(_M_state);
267  }
268 
269  [[nodiscard]]
270  bool
271  stop_requested() const noexcept
272  {
273  return stop_possible() && _M_state->_M_stop_requested();
274  }
275 
276  bool
277  request_stop() const noexcept
278  {
279  if (stop_possible())
280  return _M_state->_M_request_stop();
281  return false;
282  }
283 
284  [[nodiscard]]
285  stop_token
286  get_token() const noexcept
287  {
288  return stop_token{_M_state};
289  }
290 
291  void
292  swap(stop_source& __other) noexcept
293  {
294  _M_state.swap(__other._M_state);
295  }
296 
297  [[nodiscard]]
298  friend bool
299  operator==(const stop_source& __a, const stop_source& __b) noexcept
300  {
301  return __a._M_state == __b._M_state;
302  }
303 
304  [[nodiscard]]
305  friend bool
306  operator!=(const stop_source& __a, const stop_source& __b) noexcept
307  {
308  return __a._M_state != __b._M_state;
309  }
310 
311  friend void
312  swap(stop_source& __lhs, stop_source& __rhs) noexcept
313  {
314  __lhs.swap(__rhs);
315  }
316 
317  private:
318  stop_token::_Stop_state _M_state;
319  };
320 
321  /// A wrapper for callbacks to be run when a stop request is made.
322  template<typename _Callback>
323  class [[nodiscard]] stop_callback
324  : private stop_token::_Stop_cb
325  {
326  public:
327  using callback_type = _Callback;
328 
329  template<typename _Cb,
330  enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0>
331  explicit
332  stop_callback(const stop_token& __token, _Cb&& __cb)
333  noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
334  : _Stop_cb(&_S_execute), _M_cb(std::forward<_Cb>(__cb))
335  {
336  if (auto __state = __token._M_state)
337  {
338  if (__state->_M_stop_requested())
339  _S_execute(this); // ensures std::terminate on throw
340  else if (__state->_M_register_callback(this))
341  _M_state.swap(__state);
342  }
343  }
344 
345  template<typename _Cb,
346  enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0>
347  explicit
348  stop_callback(stop_token&& __token, _Cb&& __cb)
349  noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
350  : _Stop_cb(&_S_execute), _M_cb(std::forward<_Cb>(__cb))
351  {
352  if (auto& __state = __token._M_state)
353  {
354  if (__state->_M_stop_requested())
355  _S_execute(this); // ensures std::terminate on throw
356  else if (__state->_M_register_callback(this))
357  _M_state.swap(__state);
358  }
359  }
360 
361  ~stop_callback()
362  {
363  if (_M_state)
364  {
365  _M_state->_M_remove_callback(this);
366  }
367  }
368 
369  stop_callback(const stop_callback&) = delete;
370  stop_callback& operator=(const stop_callback&) = delete;
371  stop_callback(stop_callback&&) = delete;
372  stop_callback& operator=(stop_callback&&) = delete;
373 
374  private:
375  _Callback _M_cb;
376  stop_token::_Stop_state _M_state = nullptr;
377 
378  static void
379  _S_execute(_Stop_cb* __that) noexcept
380  {
381  static_cast<stop_callback*>(__that)->_M_cb();
382  }
383  };
384 
385  template<typename _Callback>
386  stop_callback(stop_token, _Callback) -> stop_callback<_Callback>;
387 
388 _GLIBCXX_END_NAMESPACE_VERSION
389 } // namespace
390 #endif // __cplusplus > 201703L
391 #endif // _GLIBCXX_STOP_TOKEN
std::shared_ptr
A smart pointer with reference-counted copy semantics.
Definition: bits/shared_ptr.h:121
concurrence.h
std::forward
constexpr _Tp && forward(typename std::remove_reference< _Tp >::type &__t) noexcept
Forward an lvalue.
Definition: move.h:76
std
ISO C++ entities toplevel namespace is std.
std::atomic< bool >
atomic<bool>
Definition: atomic:62
unique_ptr.h
atomic
std::mutex
The standard mutex type.
Definition: std_mutex.h:83
std::lock_guard
A simple scoped lock type.
Definition: std_mutex.h:153
std::move
constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept
Convert a value to an rvalue.
Definition: move.h:101
std_mutex.h