llvm-project
369 строк · 12.7 Кб
1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP_CONDITION_VARIABLE
11#define _LIBCPP_CONDITION_VARIABLE
12
13/*
14condition_variable synopsis
15
16namespace std
17{
18
19enum class cv_status { no_timeout, timeout };
20
21class condition_variable
22{
23public:
24condition_variable();
25~condition_variable();
26
27condition_variable(const condition_variable&) = delete;
28condition_variable& operator=(const condition_variable&) = delete;
29
30void notify_one() noexcept;
31void notify_all() noexcept;
32
33void wait(unique_lock<mutex>& lock);
34template <class Predicate>
35void wait(unique_lock<mutex>& lock, Predicate pred);
36
37template <class Clock, class Duration>
38cv_status
39wait_until(unique_lock<mutex>& lock,
40const chrono::time_point<Clock, Duration>& abs_time);
41
42template <class Clock, class Duration, class Predicate>
43bool
44wait_until(unique_lock<mutex>& lock,
45const chrono::time_point<Clock, Duration>& abs_time,
46Predicate pred);
47
48template <class Rep, class Period>
49cv_status
50wait_for(unique_lock<mutex>& lock,
51const chrono::duration<Rep, Period>& rel_time);
52
53template <class Rep, class Period, class Predicate>
54bool
55wait_for(unique_lock<mutex>& lock,
56const chrono::duration<Rep, Period>& rel_time,
57Predicate pred);
58
59typedef pthread_cond_t* native_handle_type;
60native_handle_type native_handle();
61};
62
63void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
64
65class condition_variable_any
66{
67public:
68condition_variable_any();
69~condition_variable_any();
70
71condition_variable_any(const condition_variable_any&) = delete;
72condition_variable_any& operator=(const condition_variable_any&) = delete;
73
74void notify_one() noexcept;
75void notify_all() noexcept;
76
77template <class Lock>
78void wait(Lock& lock);
79template <class Lock, class Predicate>
80void wait(Lock& lock, Predicate pred);
81
82template <class Lock, class Clock, class Duration>
83cv_status
84wait_until(Lock& lock,
85const chrono::time_point<Clock, Duration>& abs_time);
86
87template <class Lock, class Clock, class Duration, class Predicate>
88bool
89wait_until(Lock& lock,
90const chrono::time_point<Clock, Duration>& abs_time,
91Predicate pred);
92
93template <class Lock, class Rep, class Period>
94cv_status
95wait_for(Lock& lock,
96const chrono::duration<Rep, Period>& rel_time);
97
98template <class Lock, class Rep, class Period, class Predicate>
99bool
100wait_for(Lock& lock,
101const chrono::duration<Rep, Period>& rel_time,
102Predicate pred);
103
104// [thread.condvarany.intwait], interruptible waits
105template <class Lock, class Predicate>
106bool wait(Lock& lock, stop_token stoken, Predicate pred); // since C++20
107
108template <class Lock, class Clock, class Duration, class Predicate>
109bool wait_until(Lock& lock, stop_token stoken,
110const chrono::time_point<Clock, Duration>& abs_time, Predicate pred); // since C++20
111
112template <class Lock, class Rep, class Period, class Predicate>
113bool wait_for(Lock& lock, stop_token stoken,
114const chrono::duration<Rep, Period>& rel_time, Predicate pred); // since C++20
115};
116
117} // std
118
119*/
120
121#include <__chrono/duration.h>
122#include <__chrono/steady_clock.h>
123#include <__chrono/time_point.h>
124#include <__condition_variable/condition_variable.h>
125#include <__config>
126#include <__memory/shared_ptr.h>
127#include <__mutex/lock_guard.h>
128#include <__mutex/mutex.h>
129#include <__mutex/tag_types.h>
130#include <__mutex/unique_lock.h>
131#include <__stop_token/stop_callback.h>
132#include <__stop_token/stop_token.h>
133#include <__utility/move.h>
134#include <version>
135
136#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
137# pragma GCC system_header
138#endif
139
140_LIBCPP_PUSH_MACROS
141#include <__undef_macros>
142
143#ifndef _LIBCPP_HAS_NO_THREADS
144
145_LIBCPP_BEGIN_NAMESPACE_STD
146
147class _LIBCPP_EXPORTED_FROM_ABI condition_variable_any {
148condition_variable __cv_;
149shared_ptr<mutex> __mut_;
150
151public:
152_LIBCPP_HIDE_FROM_ABI condition_variable_any();
153
154_LIBCPP_HIDE_FROM_ABI void notify_one() _NOEXCEPT;
155_LIBCPP_HIDE_FROM_ABI void notify_all() _NOEXCEPT;
156
157template <class _Lock>
158_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS void wait(_Lock& __lock);
159template <class _Lock, class _Predicate>
160_LIBCPP_HIDE_FROM_ABI void wait(_Lock& __lock, _Predicate __pred);
161
162template <class _Lock, class _Clock, class _Duration>
163_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status
164wait_until(_Lock& __lock, const chrono::time_point<_Clock, _Duration>& __t);
165
166template <class _Lock, class _Clock, class _Duration, class _Predicate>
167bool _LIBCPP_HIDE_FROM_ABI
168wait_until(_Lock& __lock, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred);
169
170template <class _Lock, class _Rep, class _Period>
171cv_status _LIBCPP_HIDE_FROM_ABI wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __d);
172
173template <class _Lock, class _Rep, class _Period, class _Predicate>
174bool _LIBCPP_HIDE_FROM_ABI wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred);
175
176# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
177
178template <class _Lock, class _Predicate>
179_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool wait(_Lock& __lock, stop_token __stoken, _Predicate __pred);
180
181template <class _Lock, class _Clock, class _Duration, class _Predicate>
182_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool wait_until(
183_Lock& __lock, stop_token __stoken, const chrono::time_point<_Clock, _Duration>& __abs_time, _Predicate __pred);
184
185template <class _Lock, class _Rep, class _Period, class _Predicate>
186_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
187wait_for(_Lock& __lock, stop_token __stoken, const chrono::duration<_Rep, _Period>& __rel_time, _Predicate __pred);
188
189# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
190};
191
192inline condition_variable_any::condition_variable_any() : __mut_(make_shared<mutex>()) {}
193
194inline void condition_variable_any::notify_one() _NOEXCEPT {
195{ lock_guard<mutex> __lx(*__mut_); }
196__cv_.notify_one();
197}
198
199inline void condition_variable_any::notify_all() _NOEXCEPT {
200{ lock_guard<mutex> __lx(*__mut_); }
201__cv_.notify_all();
202}
203
204template <class _Lock>
205struct __unlock_guard {
206_Lock& __lock_;
207
208_LIBCPP_HIDE_FROM_ABI __unlock_guard(_Lock& __lock) : __lock_(__lock) { __lock_.unlock(); }
209
210_LIBCPP_HIDE_FROM_ABI ~__unlock_guard() _NOEXCEPT // turns exception to std::terminate
211{
212__lock_.lock();
213}
214
215__unlock_guard(const __unlock_guard&) = delete;
216__unlock_guard& operator=(const __unlock_guard&) = delete;
217};
218
219template <class _Lock>
220void condition_variable_any::wait(_Lock& __lock) {
221shared_ptr<mutex> __mut = __mut_;
222unique_lock<mutex> __lk(*__mut);
223__unlock_guard<_Lock> __unlock(__lock);
224lock_guard<unique_lock<mutex> > __lx(__lk, adopt_lock_t());
225__cv_.wait(__lk);
226} // __mut_.unlock(), __lock.lock()
227
228template <class _Lock, class _Predicate>
229inline void condition_variable_any::wait(_Lock& __lock, _Predicate __pred) {
230while (!__pred())
231wait(__lock);
232}
233
234template <class _Lock, class _Clock, class _Duration>
235cv_status condition_variable_any::wait_until(_Lock& __lock, const chrono::time_point<_Clock, _Duration>& __t) {
236shared_ptr<mutex> __mut = __mut_;
237unique_lock<mutex> __lk(*__mut);
238__unlock_guard<_Lock> __unlock(__lock);
239lock_guard<unique_lock<mutex> > __lx(__lk, adopt_lock_t());
240return __cv_.wait_until(__lk, __t);
241} // __mut_.unlock(), __lock.lock()
242
243template <class _Lock, class _Clock, class _Duration, class _Predicate>
244inline bool
245condition_variable_any::wait_until(_Lock& __lock, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred) {
246while (!__pred())
247if (wait_until(__lock, __t) == cv_status::timeout)
248return __pred();
249return true;
250}
251
252template <class _Lock, class _Rep, class _Period>
253inline cv_status condition_variable_any::wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __d) {
254return wait_until(__lock, chrono::steady_clock::now() + __d);
255}
256
257template <class _Lock, class _Rep, class _Period, class _Predicate>
258inline bool
259condition_variable_any::wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {
260return wait_until(__lock, chrono::steady_clock::now() + __d, std::move(__pred));
261}
262
263# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
264
265template <class _Lock, class _Predicate>
266bool condition_variable_any::wait(_Lock& __user_lock, stop_token __stoken, _Predicate __pred) {
267if (__stoken.stop_requested())
268return __pred();
269
270// Per https://eel.is/c++draft/thread.condition.condvarany#general-note-2,
271// we do need to take a copy of the shared pointer __mut_
272// This ensures that a thread can call the destructor immediately after calling
273// notify_all, without waiting all the wait calls.
274// A thread can also safely call the destructor immediately after calling
275// request_stop, as the call to request_stop would evaluate the callback,
276// which accesses the internal condition variable, immediately on the same thread.
277// In this situation, it is OK even without copying a shared ownership the internal
278// condition variable. However, this needs the evaluation of stop_callback to
279// happen-before the destruction.
280// The spec only says "Only the notification to unblock the wait needs to happen
281// before destruction". To make this work, we need to copy the shared ownership of
282// the internal condition variable inside this function, which is not possible
283// with the current ABI.
284shared_ptr<mutex> __mut = __mut_;
285
286stop_callback __cb(__stoken, [this] { notify_all(); });
287
288while (true) {
289if (__pred())
290return true;
291
292// We need to take the internal lock before checking stop_requested,
293// so that the notification cannot come in between the stop_requested
294// check and entering the wait.
295// Note that the stop_callback takes the same internal lock before notifying
296unique_lock<mutex> __internal_lock(*__mut);
297if (__stoken.stop_requested())
298break;
299
300__unlock_guard<_Lock> __unlock(__user_lock);
301unique_lock<mutex> __internal_lock2(
302std::move(__internal_lock)); // switch unlock order between __internal_lock and __user_lock
303__cv_.wait(__internal_lock2);
304} // __internal_lock2.unlock(), __user_lock.lock()
305return __pred();
306}
307
308template <class _Lock, class _Clock, class _Duration, class _Predicate>
309bool condition_variable_any::wait_until(
310_Lock& __user_lock,
311stop_token __stoken,
312const chrono::time_point<_Clock, _Duration>& __abs_time,
313_Predicate __pred) {
314if (__stoken.stop_requested())
315return __pred();
316
317shared_ptr<mutex> __mut = __mut_;
318stop_callback __cb(__stoken, [this] { notify_all(); });
319
320while (true) {
321if (__pred())
322return true;
323
324unique_lock<mutex> __internal_lock(*__mut);
325if (__stoken.stop_requested())
326break;
327
328__unlock_guard<_Lock> __unlock(__user_lock);
329unique_lock<mutex> __internal_lock2(
330std::move(__internal_lock)); // switch unlock order between __internal_lock and __user_lock
331
332if (__cv_.wait_until(__internal_lock2, __abs_time) == cv_status::timeout)
333break;
334} // __internal_lock2.unlock(), __user_lock.lock()
335return __pred();
336}
337
338template <class _Lock, class _Rep, class _Period, class _Predicate>
339bool condition_variable_any::wait_for(
340_Lock& __lock, stop_token __stoken, const chrono::duration<_Rep, _Period>& __rel_time, _Predicate __pred) {
341return wait_until(__lock, std::move(__stoken), chrono::steady_clock::now() + __rel_time, std::move(__pred));
342}
343
344# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
345
346_LIBCPP_EXPORTED_FROM_ABI void notify_all_at_thread_exit(condition_variable&, unique_lock<mutex>);
347
348_LIBCPP_END_NAMESPACE_STD
349
350#endif // !_LIBCPP_HAS_NO_THREADS
351
352_LIBCPP_POP_MACROS
353
354#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
355# include <atomic>
356# include <concepts>
357# include <cstdint>
358# include <cstdlib>
359# include <cstring>
360# include <initializer_list>
361# include <iosfwd>
362# include <new>
363# include <stdexcept>
364# include <system_error>
365# include <type_traits>
366# include <typeinfo>
367#endif
368
369#endif // _LIBCPP_CONDITION_VARIABLE
370