libstdc++
stop_token
Go to the documentation of this file.
1 // <stop_token> -*- C++ -*-
2 
3 // Copyright (C) 2019-2020 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  { return __a._M_state == __b._M_state; }
91 
92  friend void
93  swap(stop_token& __lhs, stop_token& __rhs) noexcept
94  { __lhs.swap(__rhs); }
95 
96  private:
97  friend class stop_source;
98  template<typename _Callback>
99  friend class stop_callback;
100 
101  struct _Stop_cb
102  {
103  void(*_M_callback)(_Stop_cb*);
104  _Stop_cb* _M_prev = nullptr;
105  _Stop_cb* _M_next = nullptr;
106 
107  template<typename _Cb>
108  _Stop_cb(_Cb&& __cb)
109  : _M_callback(std::forward<_Cb>(__cb))
110  { }
111 
112  bool
113  _M_linked() const noexcept
114  {
115  return (_M_prev != nullptr)
116  || (_M_next != nullptr);
117  }
118 
119  static void
120  _S_execute(_Stop_cb* __cb) noexcept
121  {
122  __cb->_M_callback(__cb);
123  __cb->_M_prev = __cb->_M_next = nullptr;
124  }
125  };
126 
127  struct _Stop_state_t
128  {
129  std::atomic<bool> _M_stopped{false};
130  _Stop_cb* _M_head = nullptr;
131 #ifdef _GLIBCXX_HAS_GTHREADS
132  std::mutex _M_mtx;
133 #endif
134 
135  _Stop_state_t() = default;
136 
137  bool
138  _M_stop_requested() noexcept
139  {
140  return _M_stopped;
141  }
142 
143  bool
144  _M_request_stop()
145  {
146  bool __stopped = false;
147  if (_M_stopped.compare_exchange_strong(__stopped, true))
148  {
149 #ifdef _GLIBCXX_HAS_GTHREADS
150  std::lock_guard<std::mutex> __lck{_M_mtx};
151 #endif
152  while (_M_head)
153  {
154  auto __p = _M_head;
155  _M_head = _M_head->_M_next;
156  _Stop_cb::_S_execute(__p);
157  }
158  return true;
159  }
160  return false;
161  }
162 
163  bool
164  _M_register_callback(_Stop_cb* __cb)
165  {
166 #ifdef _GLIBCXX_HAS_GTHREADS
167  std::lock_guard<std::mutex> __lck{_M_mtx};
168 #endif
169  if (_M_stopped)
170  return false;
171 
172  __cb->_M_next = _M_head;
173  if (_M_head)
174  {
175  _M_head->_M_prev = __cb;
176  }
177  _M_head = __cb;
178  return true;
179  }
180 
181  void
182  _M_remove_callback(_Stop_cb* __cb)
183  {
184 #ifdef _GLIBCXX_HAS_GTHREADS
185  std::lock_guard<std::mutex> __lck{_M_mtx};
186 #endif
187  if (__cb == _M_head)
188  {
189  _M_head = _M_head->_M_next;
190  if (_M_head)
191  {
192  _M_head->_M_prev = nullptr;
193  }
194  }
195  else if (!__cb->_M_linked())
196  {
197  return;
198  }
199  else
200  {
201  __cb->_M_prev->_M_next = __cb->_M_next;
202  if (__cb->_M_next)
203  {
204  __cb->_M_next->_M_prev = __cb->_M_prev;
205  }
206  }
207  }
208  };
209 
210  using _Stop_state = std::shared_ptr<_Stop_state_t>;
211  _Stop_state _M_state;
212 
213  explicit
214  stop_token(const _Stop_state& __state) noexcept
215  : _M_state{__state}
216  { }
217  };
218 
219  /// A type that allows a stop request to be made.
220  class stop_source
221  {
222  public:
223  stop_source()
224  : _M_state(std::make_shared<stop_token::_Stop_state_t>())
225  { }
226 
227  explicit stop_source(std::nostopstate_t) noexcept
228  { }
229 
230  stop_source(const stop_source& __other) noexcept
231  : _M_state(__other._M_state)
232  { }
233 
234  stop_source(stop_source&& __other) noexcept
235  : _M_state(std::move(__other._M_state))
236  { }
237 
238  stop_source&
239  operator=(const stop_source& __rhs) noexcept
240  {
241  if (_M_state != __rhs._M_state)
242  _M_state = __rhs._M_state;
243  return *this;
244  }
245 
246  stop_source&
247  operator=(stop_source&& __rhs) noexcept
248  {
249  std::swap(_M_state, __rhs._M_state);
250  return *this;
251  }
252 
253  [[nodiscard]]
254  bool
255  stop_possible() const noexcept
256  {
257  return static_cast<bool>(_M_state);
258  }
259 
260  [[nodiscard]]
261  bool
262  stop_requested() const noexcept
263  {
264  return stop_possible() && _M_state->_M_stop_requested();
265  }
266 
267  bool
268  request_stop() const noexcept
269  {
270  if (stop_possible())
271  return _M_state->_M_request_stop();
272  return false;
273  }
274 
275  [[nodiscard]]
276  stop_token
277  get_token() const noexcept
278  {
279  return stop_token{_M_state};
280  }
281 
282  void
283  swap(stop_source& __other) noexcept
284  {
285  _M_state.swap(__other._M_state);
286  }
287 
288  [[nodiscard]]
289  friend bool
290  operator==(const stop_source& __a, const stop_source& __b) noexcept
291  {
292  return __a._M_state == __b._M_state;
293  }
294 
295  friend void
296  swap(stop_source& __lhs, stop_source& __rhs) noexcept
297  {
298  __lhs.swap(__rhs);
299  }
300 
301  private:
302  stop_token::_Stop_state _M_state;
303  };
304 
305  /// A wrapper for callbacks to be run when a stop request is made.
306  template<typename _Callback>
307  class [[nodiscard]] stop_callback
308  : private stop_token::_Stop_cb
309  {
310  public:
311  using callback_type = _Callback;
312 
313  template<typename _Cb,
314  enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0>
315  explicit
316  stop_callback(const stop_token& __token, _Cb&& __cb)
317  noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
318  : _Stop_cb(&_S_execute), _M_cb(std::forward<_Cb>(__cb))
319  {
320  if (auto __state = __token._M_state)
321  {
322  if (__state->_M_stop_requested())
323  _S_execute(this); // ensures std::terminate on throw
324  else if (__state->_M_register_callback(this))
325  _M_state.swap(__state);
326  }
327  }
328 
329  template<typename _Cb,
330  enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0>
331  explicit
332  stop_callback(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  ~stop_callback()
346  {
347  if (_M_state)
348  {
349  _M_state->_M_remove_callback(this);
350  }
351  }
352 
353  stop_callback(const stop_callback&) = delete;
354  stop_callback& operator=(const stop_callback&) = delete;
355  stop_callback(stop_callback&&) = delete;
356  stop_callback& operator=(stop_callback&&) = delete;
357 
358  private:
359  _Callback _M_cb;
360  stop_token::_Stop_state _M_state = nullptr;
361 
362  static void
363  _S_execute(_Stop_cb* __that) noexcept
364  {
365  static_cast<stop_callback*>(__that)->_M_cb();
366  }
367  };
368 
369  template<typename _Callback>
370  stop_callback(stop_token, _Callback) -> stop_callback<_Callback>;
371 
372 _GLIBCXX_END_NAMESPACE_VERSION
373 } // namespace
374 #endif // __cplusplus > 201703L
375 #endif // _GLIBCXX_STOP_TOKEN