Skip to content

Commit 4495d63

Browse files
committed
[Threading] Add ConditionVariable support.
Swift Concurrency would like to be able to use condition variables. Add support to the threading packages. rdar://100236038
1 parent b104064 commit 4495d63

File tree

12 files changed

+766
-50
lines changed

12 files changed

+766
-50
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===--- ConditionVariable.h - ConditionVariable -------------- -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Provides a system-independent condition variable, Condition.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_THREADING_CONDITIONVARIABLE_H
18+
#define SWIFT_THREADING_CONDITIONVARIABLE_H
19+
20+
#include "ScopedLock.h"
21+
22+
#include "Impl.h"
23+
24+
namespace swift {
25+
26+
// A condition variable.
27+
//
28+
// Note that use of this class should be minimised because there is no
29+
// automatic way to deal with priority inversions; the operating system
30+
// cannot, in general, know which thread(s) might wake a thread that is
31+
// waiting on a condition variable.
32+
//
33+
// FWIW: the reason this class doesn't use a separate Mutex instance is
34+
// that on Darwin, Mutex uses os_unfair_lock, and there is no condition
35+
// variable implementation that takes one of those.
36+
class ConditionVariable {
37+
38+
ConditionVariable(const ConditionVariable &) = delete;
39+
ConditionVariable &operator=(const ConditionVariable &) = delete;
40+
ConditionVariable(ConditionVariable &&) = delete;
41+
ConditionVariable &operator=(ConditionVariable &&) = delete;
42+
43+
public:
44+
/// Constructs a condition variable.
45+
ConditionVariable() {
46+
threading_impl::cond_init(Handle);
47+
}
48+
~ConditionVariable() {
49+
threading_impl::cond_destroy(Handle);
50+
}
51+
52+
/// Lock the associated mutex.
53+
///
54+
/// Precondition: ConditionVariable not locked by this thread.
55+
void lock() {
56+
threading_impl::cond_lock(Handle);
57+
}
58+
59+
/// Unlock the associated mutex.
60+
///
61+
/// Precondition: ConditionVariable locked by this thread.
62+
void unlock() {
63+
threading_impl::cond_unlock(Handle);
64+
}
65+
66+
/// Unblock a single thread that is blocked on this condition variable.
67+
void signal() {
68+
threading_impl::cond_signal(Handle);
69+
}
70+
71+
/// Unblock all threads that are blocked on this condition variable.
72+
void broadcast() {
73+
threading_impl::cond_broadcast(Handle);
74+
}
75+
76+
/// Unlock and block on this condition variable.
77+
///
78+
/// After this method returns, the associated mutex will be locked.
79+
///
80+
/// Precondition: ConditionVariable locked by this thread.
81+
void wait() {
82+
threading_impl::cond_wait(Handle);
83+
}
84+
85+
/// Unlock and block on this condition variable, with a timeout.
86+
///
87+
/// After this method returns, the associated mutex will be locked.
88+
///
89+
/// Precondition: ConditionVariable locked by this thread.
90+
template <class Rep, class Period>
91+
bool wait(std::chrono::duration<Rep, Period> duration) {
92+
return threading_impl::cond_wait(Handle, duration);
93+
}
94+
95+
/// Unlock and block on this condition variable, with a deadline.
96+
///
97+
/// After this method returns, the associated mutex will be locked.
98+
///
99+
/// Precondition: ConditionVariable locked by this thread.
100+
template <class Rep, class Period>
101+
bool waitUntil(std::chrono::time_point<std::chrono::system_clock,
102+
std::chrono::duration<Rep, Period>> deadline) {
103+
auto sysdeadline = std::chrono::time_point_cast<
104+
std::chrono::system_clock::duration>(deadline);
105+
return threading_impl::cond_wait(Handle, sysdeadline);
106+
}
107+
108+
/// Acquires lock before calling the supplied critical section and releases
109+
/// lock on return from critical section.
110+
///
111+
/// This call can block while waiting for the lock to become available.
112+
///
113+
/// For example the following mutates value while holding the mutex lock.
114+
///
115+
/// ```
116+
/// cond.withLock([&value] { value++; });
117+
/// ```
118+
///
119+
/// Precondition: ConditionVariable not locked by this thread.
120+
template <typename CriticalSection>
121+
auto withLock(CriticalSection &&criticalSection)
122+
-> decltype(std::forward<CriticalSection>(criticalSection)()) {
123+
ScopedLock guard(*this);
124+
return std::forward<CriticalSection>(criticalSection)();
125+
}
126+
127+
/// A stack based object that locks the supplied ConditionVariable on
128+
/// construction and unlocks it on destruction.
129+
///
130+
/// Precondition: ConditionVariable not locked by this thread.
131+
typedef ScopedLockT<ConditionVariable, false> ScopedLock;
132+
133+
/// A stack based object that unlocks the supplied ConditionVariable on
134+
/// construction and locks it on destruction.
135+
///
136+
/// Precondition: ConditionVariable locked by this thread.
137+
typedef ScopedLockT<ConditionVariable, true> ScopedUnlock;
138+
139+
private:
140+
threading_impl::cond_handle Handle;
141+
};
142+
143+
}
144+
145+
#endif // SWIFT_THREADING_CONDITIONVARIABLE_H

include/swift/Threading/Impl/C11.h

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define SWIFT_THREADING_IMPL_C11_H
1919

2020
#include <atomic>
21+
#include <chrono>
2122
#include <cstdint>
2223

2324
#include <threads.h>
@@ -36,13 +37,13 @@ namespace threading_impl {
3637
swift::threading::fatal(#expr " failed with error %d\n", res_); \
3738
} while (0)
3839

39-
#define SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(expr) \
40+
#define SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
4041
do { \
4142
int res_ = (expr); \
4243
switch (res_) { \
4344
case thrd_success: \
4445
return true; \
45-
case thrd_busy: \
46+
case falseerr: \
4647
return false; \
4748
default: \
4849
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
@@ -78,7 +79,7 @@ inline void mutex_unlock(mutex_handle &handle) {
7879
SWIFT_C11THREADS_CHECK(::mtx_unlock(&handle));
7980
}
8081
inline bool mutex_try_lock(mutex_handle &handle) {
81-
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(::mtx_trylock(&handle));
82+
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(thrd_busy, ::mtx_trylock(&handle));
8283
}
8384

8485
inline void mutex_unsafe_lock(mutex_handle &handle) {
@@ -134,7 +135,7 @@ inline void lazy_mutex_unlock(lazy_mutex_handle &handle) {
134135
}
135136
inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) {
136137
lazy_mutex_init(handle);
137-
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(::mtx_trylock(&handle.mutex));
138+
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(thrd_busy, ::mtx_trylock(&handle.mutex));
138139
}
139140

140141
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {
@@ -146,6 +147,53 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
146147
(void)::mtx_unlock(&handle.mutex);
147148
}
148149

150+
// .. ConditionVariable support ..............................................
151+
152+
struct cond_handle {
153+
::cnd_t condition;
154+
::mtx_t mutex;
155+
};
156+
157+
inline void cond_init(cond_handle &handle) {
158+
SWIFT_C11THREADS_CHECK(::cnd_init(&handle.condition));
159+
SWIFT_C11THREADS_CHECK(::mtx_init(&handle.mutex, ::mtx_plain));
160+
}
161+
inline void cond_destroy(cond_handle &handle) {
162+
::cnd_destroy(&handle.condition);
163+
::mtx_destroy(&handle.mutex);
164+
}
165+
inline void cond_lock(cond_handle &handle) {
166+
SWIFT_C11THREADS_CHECK(::mtx_lock(&handle.mutex));
167+
}
168+
inline void cond_unlock(cond_handle &handle) {
169+
SWIFT_C11THREADS_CHECK(::mtx_unlock(&handle.mutex));
170+
}
171+
inline void cond_signal(cond_handle &handle) {
172+
SWIFT_C11THREADS_CHECK(::cnd_signal(&handle.condition));
173+
}
174+
inline void cond_broadcast(cond_handle &handle) {
175+
SWIFT_C11THREADS_CHECK(::cnd_broadcast(&handle.condition));
176+
}
177+
inline void cond_wait(cond_handle &handle) {
178+
SWIFT_C11THREADS_CHECK(::cnd_wait(&handle.condition, &handle.mutex));
179+
}
180+
template <class Rep, class Period>
181+
inline bool cond_wait(cond_handle &handle,
182+
std::chrono::duration<Rep, Period> duration) {
183+
auto deadline = std::chrono::system_clock::now() + duration;
184+
return cond_wait(handle, deadline);
185+
}
186+
inline bool cond_wait(cond_handle &handle,
187+
std::chrono::system_clock::time_point deadline) {
188+
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
189+
deadline.time_since_epoch()).count();
190+
struct ::timespec ts = { ::time_t(ns / 1000000000), long(ns % 1000000000) };
191+
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(
192+
thrd_timedout,
193+
::cnd_timedwait(&handle.condition, &handle.mutex, &ts)
194+
);
195+
}
196+
149197
// .. Once ...................................................................
150198

151199
typedef std::atomic<std::intptr_t> once_t;

include/swift/Threading/Impl/Darwin.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,35 @@
2121
#include <os/lock.h>
2222
#include <pthread.h>
2323

24+
#include <chrono>
25+
2426
#include "llvm/ADT/Optional.h"
2527

2628
#include "swift/Threading/Errors.h"
2729

2830
namespace swift {
2931
namespace threading_impl {
3032

33+
#define SWIFT_PTHREADS_CHECK(expr) \
34+
do { \
35+
int res_ = (expr); \
36+
if (res_ != 0) \
37+
swift::threading::fatal(#expr " failed with error %d\n", res_); \
38+
} while (0)
39+
40+
#define SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
41+
do { \
42+
int res_ = (expr); \
43+
switch (res_) { \
44+
case 0: \
45+
return true; \
46+
case falseerr: \
47+
return false; \
48+
default: \
49+
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
50+
} \
51+
} while (0)
52+
3153
// .. Thread related things ..................................................
3254

3355
using thread_id = ::pthread_t;
@@ -102,6 +124,55 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
102124
::os_unfair_lock_unlock(&handle);
103125
}
104126

127+
// .. ConditionVariable support ..............................................
128+
129+
struct cond_handle {
130+
::pthread_cond_t condition;
131+
::pthread_mutex_t mutex;
132+
};
133+
134+
inline void cond_init(cond_handle &handle) {
135+
handle.condition = PTHREAD_COND_INITIALIZER;
136+
handle.mutex = PTHREAD_MUTEX_INITIALIZER;
137+
}
138+
inline void cond_destroy(cond_handle &handle) {
139+
SWIFT_PTHREADS_CHECK(::pthread_cond_destroy(&handle.condition));
140+
SWIFT_PTHREADS_CHECK(::pthread_mutex_destroy(&handle.mutex));
141+
}
142+
inline void cond_lock(cond_handle &handle) {
143+
SWIFT_PTHREADS_CHECK(::pthread_mutex_lock(&handle.mutex));
144+
}
145+
inline void cond_unlock(cond_handle &handle) {
146+
SWIFT_PTHREADS_CHECK(::pthread_mutex_unlock(&handle.mutex));
147+
}
148+
inline void cond_signal(cond_handle &handle) {
149+
SWIFT_PTHREADS_CHECK(::pthread_cond_signal(&handle.condition));
150+
}
151+
inline void cond_broadcast(cond_handle &handle) {
152+
SWIFT_PTHREADS_CHECK(::pthread_cond_broadcast(&handle.condition));
153+
}
154+
inline void cond_wait(cond_handle &handle) {
155+
SWIFT_PTHREADS_CHECK(::pthread_cond_wait(&handle.condition, &handle.mutex));
156+
}
157+
template <class Rep, class Period>
158+
inline bool cond_wait(cond_handle &handle,
159+
std::chrono::duration<Rep, Period> duration) {
160+
auto deadline = std::chrono::time_point_cast<
161+
std::chrono::system_clock::duration>(std::chrono::system_clock::now()
162+
+ duration);
163+
return cond_wait(handle, deadline);
164+
}
165+
inline bool cond_wait(cond_handle &handle,
166+
std::chrono::system_clock::time_point deadline) {
167+
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
168+
deadline.time_since_epoch()).count();
169+
struct ::timespec ts = { ::time_t(ns / 1000000000), long(ns % 1000000000) };
170+
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(
171+
ETIMEDOUT,
172+
::pthread_cond_timedwait(&handle.condition, &handle.mutex, &ts)
173+
);
174+
}
175+
105176
// .. Once ...................................................................
106177

107178
using once_t = ::dispatch_once_t;

0 commit comments

Comments
 (0)