Skip to content

Commit 4495d63

Browse files
committedSep 27, 2022
[Threading] Add ConditionVariable support.
Swift Concurrency would like to be able to use condition variables. Add support to the threading packages. rdar://100236038

File tree

12 files changed

+766
-50
lines changed

12 files changed

+766
-50
lines changed
 
+145
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

+52-4
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

+71
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;

‎include/swift/Threading/Impl/Linux.h

+57-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//==--- Pthreads.h - Threading abstraction implementation ------ -*-C++ -*-===//
1+
//==--- Linux.h - Threading abstraction implementation --------- -*-C++ -*-===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -21,6 +21,7 @@
2121
#include <pthread.h>
2222

2323
#include <atomic>
24+
#include <chrono>
2425
#include <optional>
2526

2627
#include "llvm/ADT/Optional.h"
@@ -39,13 +40,13 @@ namespace threading_impl {
3940
swift::threading::fatal(#expr " failed with error %d\n", res_); \
4041
} while (0)
4142

42-
#define SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(expr) \
43+
#define SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
4344
do { \
4445
int res_ = (expr); \
4546
switch (res_) { \
4647
case 0: \
4748
return true; \
48-
case EBUSY: \
49+
case falseerr: \
4950
return false; \
5051
default: \
5152
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
@@ -93,7 +94,8 @@ inline void mutex_unlock(mutex_handle &handle) {
9394
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_unlock(&handle));
9495
}
9596
inline bool mutex_try_lock(mutex_handle &handle) {
96-
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
97+
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(EBUSY,
98+
::pthread_mutex_trylock(&handle));
9799
}
98100

99101
inline void mutex_unsafe_lock(mutex_handle &handle) {
@@ -121,7 +123,8 @@ inline void lazy_mutex_unlock(lazy_mutex_handle &handle) {
121123
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_unlock(&handle));
122124
}
123125
inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) {
124-
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
126+
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(EBUSY,
127+
::pthread_mutex_trylock(&handle));
125128
}
126129

127130
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {
@@ -131,6 +134,55 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
131134
(void)::pthread_mutex_unlock(&handle);
132135
}
133136

137+
// .. ConditionVariable support ..............................................
138+
139+
struct cond_handle {
140+
::pthread_cond_t condition;
141+
::pthread_mutex_t mutex;
142+
};
143+
144+
inline void cond_init(cond_handle &handle) {
145+
handle.condition = PTHREAD_COND_INITIALIZER;
146+
handle.mutex = PTHREAD_MUTEX_INITIALIZER;
147+
}
148+
inline void cond_destroy(cond_handle &handle) {
149+
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_destroy(&handle.condition));
150+
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_destroy(&handle.mutex));
151+
}
152+
inline void cond_lock(cond_handle &handle) {
153+
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_lock(&handle.mutex));
154+
}
155+
inline void cond_unlock(cond_handle &handle) {
156+
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_unlock(&handle.mutex));
157+
}
158+
inline void cond_signal(cond_handle &handle) {
159+
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_signal(&handle.condition));
160+
}
161+
inline void cond_broadcast(cond_handle &handle) {
162+
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_broadcast(&handle.condition));
163+
}
164+
inline void cond_wait(cond_handle &handle) {
165+
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_wait(&handle.condition, &handle.mutex));
166+
}
167+
template <class Rep, class Period>
168+
inline bool cond_wait(cond_handle &handle,
169+
std::chrono::duration<Rep, Period> duration) {
170+
auto deadline = std::chrono::time_point_cast<
171+
std::chrono::system_clock::duration>(std::chrono::system_clock::now()
172+
+ duration);
173+
return cond_wait(handle, deadline);
174+
}
175+
inline bool cond_wait(cond_handle &handle,
176+
std::chrono::system_clock::time_point deadline) {
177+
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
178+
deadline.time_since_epoch()).count();
179+
struct ::timespec ts = { ::time_t(ns / 1000000000), long(ns % 1000000000) };
180+
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(
181+
ETIMEDOUT,
182+
::pthread_cond_timedwait(&handle.condition, &handle.mutex, &ts)
183+
);
184+
}
185+
134186
// .. Once ...................................................................
135187

136188
struct once_t {

‎include/swift/Threading/Impl/Nothreads.h

+23
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include "llvm/ADT/Optional.h"
2121

22+
#include <chrono>
23+
2224
namespace swift {
2325
namespace threading_impl {
2426

@@ -57,6 +59,27 @@ inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) { return true; }
5759
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {}
5860
inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {}
5961

62+
// .. ConditionVariable support ..............................................
63+
64+
using cond_handle = unsigned;
65+
66+
inline void cond_init(cond_handle &handle) {}
67+
inline void cond_destroy(cond_handle &handle) {}
68+
inline void cond_lock(cond_handle &handle) {}
69+
inline void cond_unlock(cond_handle &handle) {}
70+
inline void cond_signal(cond_handle &handle) {}
71+
inline void cond_broadcast(cond_handle &handle) {}
72+
inline void cond_wait(cond_handle &handle) {}
73+
template <class Rep, class Period>
74+
inline bool cond_wait(cond_handle &handle,
75+
std::chrono::duration<Rep, Period> duration) {
76+
return true;
77+
}
78+
inline bool cond_wait(cond_handle &handle,
79+
std::chrono::system_clock::time_point deadline) {
80+
return true;
81+
}
82+
6083
// .. Once ...................................................................
6184

6285
typedef bool once_t;

‎include/swift/Threading/Impl/Pthreads.h

+54-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <pthread.h>
2222

2323
#include <atomic>
24+
#include <chrono>
2425
#include <cstdint>
2526

2627
#include "llvm/ADT/Optional.h"
@@ -37,13 +38,13 @@ namespace threading_impl {
3738
swift::threading::fatal(#expr " failed with error %d\n", res_); \
3839
} while (0)
3940

40-
#define SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(expr) \
41+
#define SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
4142
do { \
4243
int res_ = (expr); \
4344
switch (res_) { \
4445
case 0: \
4546
return true; \
46-
case EBUSY: \
47+
case falseerr: \
4748
return false; \
4849
default: \
4950
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
@@ -91,7 +92,7 @@ inline void mutex_unlock(mutex_handle &handle) {
9192
SWIFT_PTHREADS_CHECK(::pthread_mutex_unlock(&handle));
9293
}
9394
inline bool mutex_try_lock(mutex_handle &handle) {
94-
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
95+
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(EBUSY, ::pthread_mutex_trylock(&handle));
9596
}
9697

9798
inline void mutex_unsafe_lock(mutex_handle &handle) {
@@ -119,7 +120,7 @@ inline void lazy_mutex_unlock(lazy_mutex_handle &handle) {
119120
SWIFT_PTHREADS_CHECK(::pthread_mutex_unlock(&handle));
120121
}
121122
inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) {
122-
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
123+
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(EBUSY, ::pthread_mutex_trylock(&handle));
123124
}
124125

125126
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {
@@ -129,6 +130,55 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
129130
(void)::pthread_mutex_unlock(&handle);
130131
}
131132

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

134184
using once_t = std::atomic<std::intptr_t>;

‎include/swift/Threading/Impl/Win32.h

+49
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "Win32/Win32Defs.h"
2121

22+
#include <chrono>
2223
#include <atomic>
2324

2425
#include "llvm/ADT/Optional.h"
@@ -86,6 +87,54 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
8687
ReleaseSRWLockExclusive(&handle);
8788
}
8889

90+
// .. ConditionVariable support ..............................................
91+
92+
struct cond_handle {
93+
SWIFT_CONDITION_VARIABLE condition;
94+
SWIFT_SRWLOCK lock;
95+
};
96+
97+
inline void cond_init(cond_handle &handle) {
98+
handle.condition = CONDITION_VARIABLE_INIT;
99+
handle.lock = SRWLOCK_INIT;
100+
}
101+
inline void cond_destroy(cond_handle &handle) {}
102+
inline void cond_lock(cond_handle &handle) {
103+
AcquireSRWLockExclusive(&handle.lock);
104+
}
105+
inline void cond_unlock(cond_handle &handle) {
106+
ReleaseSRWLockExclusive(&handle.lock);
107+
}
108+
inline void cond_signal(cond_handle &handle) {
109+
WakeConditionVariable(&handle.condition);
110+
}
111+
inline void cond_broadcast(cond_handle &handle) {
112+
WakeAllConditionVariable(&handle.condition);
113+
}
114+
inline void cond_wait(cond_handle &handle) {
115+
SleepConditionVariableSRW(&handle.condition,
116+
&handle.lock,
117+
INFINITE,
118+
0);
119+
}
120+
template <class Rep, class Period>
121+
inline bool cond_wait(cond_handle &handle,
122+
std::chrono::duration<Rep, Period> duration) {
123+
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
124+
return SleepConditionVariableSRW(&handle.condition,
125+
&handle.lock,
126+
DWORD(ms.count()),
127+
0);
128+
}
129+
inline bool cond_wait(cond_handle &handle,
130+
std::chrono::system_clock::time_point deadline) {
131+
std::chrono::system_clock::duration duration =
132+
deadline - std::chrono::system_clock::now();
133+
if (duration < std::chrono::system_clock::duration::zero())
134+
duration = std::chrono::system_clock::duration::zero();
135+
return cond_wait(handle, duration);
136+
}
137+
89138
// .. Once ...................................................................
90139

91140
typedef std::atomic<intptr_t> once_t;

‎include/swift/Threading/Impl/Win32/Win32Defs.h

+55
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,30 @@ typedef unsigned char BYTE;
3636
typedef BYTE BOOLEAN;
3737
typedef int BOOL;
3838
typedef unsigned long DWORD;
39+
typedef unsigned long ULONG;
3940

4041
typedef VOID(NTAPI *PFLS_CALLBACK_FUNCTION)(PVOID lpFlsData);
4142

4243
typedef struct _RTL_SRWLOCK *PRTL_SRWLOCK;
4344
typedef PRTL_SRWLOCK PSRWLOCK;
4445

46+
typedef struct _RTL_CONDITION_VARIABLE *PRTL_CONDITION_VARIABLE;
47+
typedef PRTL_CONDITION_VARIABLE PCONDITION_VARIABLE;
48+
4549
// These have to be #defines, to avoid problems with <windows.h>
4650
#define RTL_SRWLOCK_INIT \
4751
{ 0 }
4852
#define SRWLOCK_INIT RTL_SRWLOCK_INIT
4953
#define FLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
5054

55+
#define RTL_CONDITION_VARIABLE_INIT {0}
56+
#define CONDITION_VARIABLE_INIT RTL_CONDITION_VARIABLE_INIT
57+
58+
#define RTL_CONDITION_VARIABLE_LOCKMODE_SHARED 0x1
59+
#define CONDITION_VARIABLE_LOCKMODE_SHARED RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
60+
61+
#define INFINITE 0xFFFFFFFF // Infinite timeout
62+
5163
extern "C" {
5264
WINBASEAPI DWORD WINAPI GetCurrentThreadId(VOID);
5365

@@ -56,6 +68,22 @@ WINBASEAPI VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
5668
WINBASEAPI VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK SRWLock);
5769
WINBASEAPI BOOLEAN WINAPI TryAcquireSRWLockExclusive(PSRWLOCK SRWLock);
5870

71+
WINBASEAPI VOID WINAPI InitializeConditionVariable(
72+
PCONDITION_VARIABLE ConditionVariable
73+
);
74+
WINBASEAPI VOID WINAPI WakeConditionVariable(
75+
PCONDITION_VARIABLE ConditionVariable
76+
);
77+
WINBASEAPI VOID WINAPI WakeAllConditionVariable(
78+
PCONDITION_VARIABLE ConditionVariable
79+
);
80+
WINBASEAPI BOOL WINAPI SleepConditionVariableSRW(
81+
PCONDITION_VARIABLE ConditionVariable,
82+
PSRWLOCK SRWLock,
83+
DWORD dwMilliseconds,
84+
ULONG Flags
85+
);
86+
5987
WINBASEAPI DWORD WINAPI FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback);
6088
WINBASEAPI PVOID WINAPI FlsGetValue(DWORD dwFlsIndex);
6189
WINBASEAPI BOOL WINAPI FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData);
@@ -86,6 +114,33 @@ inline BOOLEAN TryAcquireSRWLockExclusive(PSWIFT_SRWLOCK SRWLock) {
86114
return ::TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(SRWLock));
87115
}
88116

117+
// Similarly we have the same problem with _RTL_CONDITION_VARIABLE
118+
struct SWIFT_CONDITION_VARIABLE {
119+
PVOID Ptr;
120+
};
121+
122+
typedef SWIFT_CONDITION_VARIABLE *PSWIFT_CONDITION_VARIABLE;
123+
124+
inline VOID InitializeConditionVariable(PSWIFT_CONDITION_VARIABLE CondVar) {
125+
::InitializeConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(CondVar));
126+
}
127+
inline VOID WakeConditionVariable(PSWIFT_CONDITION_VARIABLE CondVar) {
128+
::WakeConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(CondVar));
129+
}
130+
inline VOID WakeAllConditionVariable(PSWIFT_CONDITION_VARIABLE CondVar) {
131+
::WakeAllConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(CondVar));
132+
}
133+
inline BOOL SleepConditionVariableSRW(PSWIFT_CONDITION_VARIABLE CondVar,
134+
PSWIFT_SRWLOCK SRWLock,
135+
DWORD dwMilliseconds,
136+
ULONG Flags) {
137+
return ::SleepConditionVariableSRW(
138+
reinterpret_cast<PCONDITION_VARIABLE>(CondVar),
139+
reinterpret_cast<PSRWLOCK>(SRWLock),
140+
dwMilliseconds,
141+
Flags);
142+
}
143+
89144
} // namespace threading_impl
90145
} // namespace swift
91146

‎include/swift/Threading/Mutex.h

+3-37
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
//
13-
// Provides a system-independent Mutex abstraction, as well as some
14-
// related utilities like ScopedLock.
13+
// Provides a system-independent Mutex abstraction.
1514
//
1615
//===----------------------------------------------------------------------===//
1716

@@ -21,45 +20,12 @@
2120
#include <type_traits>
2221
#include <utility>
2322

23+
#include "ScopedLock.h"
24+
2425
#include "Impl.h"
2526

2627
namespace swift {
2728

28-
// -- ScopedLock ---------------------------------------------------------------
29-
30-
/// Compile time adjusted stack based object that locks/unlocks the supplied
31-
/// Mutex type. Use the provided typedefs instead of this directly.
32-
template <typename T, bool Inverted>
33-
class ScopedLockT {
34-
ScopedLockT() = delete;
35-
ScopedLockT(const ScopedLockT &) = delete;
36-
ScopedLockT &operator=(const ScopedLockT &) = delete;
37-
ScopedLockT(ScopedLockT &&) = delete;
38-
ScopedLockT &operator=(ScopedLockT &&) = delete;
39-
40-
public:
41-
explicit ScopedLockT(T &l) : Lock(l) {
42-
if (Inverted) {
43-
Lock.unlock();
44-
} else {
45-
Lock.lock();
46-
}
47-
}
48-
49-
~ScopedLockT() {
50-
if (Inverted) {
51-
Lock.lock();
52-
} else {
53-
Lock.unlock();
54-
}
55-
}
56-
57-
private:
58-
T &Lock;
59-
};
60-
61-
// -- Mutex --------------------------------------------------------------------
62-
6329
/// A Mutex object that supports `BasicLockable` and `Lockable` C++ concepts.
6430
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
6531
/// See http://en.cppreference.com/w/cpp/concept/Lockable

‎include/swift/Threading/ScopedLock.h

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===--- ScopedLock.h - ScopedLock ---------------------------- -*- 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 ScopedLockT utility template that is used by Mutex and
14+
// ConditionVariable.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_THREADING_SCOPEDLOCK_H
19+
#define SWIFT_THREADING_SCOPEDLOCK_H
20+
21+
namespace swift {
22+
23+
// -- ScopedLock ---------------------------------------------------------------
24+
25+
/// Compile time adjusted stack based object that locks/unlocks the supplied
26+
/// Mutex type. Use the provided typedefs instead of this directly.
27+
template <typename T, bool Inverted>
28+
class ScopedLockT {
29+
ScopedLockT() = delete;
30+
ScopedLockT(const ScopedLockT &) = delete;
31+
ScopedLockT &operator=(const ScopedLockT &) = delete;
32+
ScopedLockT(ScopedLockT &&) = delete;
33+
ScopedLockT &operator=(ScopedLockT &&) = delete;
34+
35+
public:
36+
explicit ScopedLockT(T &l) : Lock(l) {
37+
if (Inverted) {
38+
Lock.unlock();
39+
} else {
40+
Lock.lock();
41+
}
42+
}
43+
44+
~ScopedLockT() {
45+
if (Inverted) {
46+
Lock.lock();
47+
} else {
48+
Lock.unlock();
49+
}
50+
}
51+
52+
private:
53+
T &Lock;
54+
};
55+
56+
} // namespace swift
57+
58+
#endif // SWIFT_THREADING_SCOPEDLOCK_H

‎unittests/Threading/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND
22
("${SWIFT_HOST_VARIANT_ARCH}" STREQUAL "${SWIFT_PRIMARY_VARIANT_ARCH}"))
33
add_swift_unittest(SwiftThreadingTests
44
Mutex.cpp
5+
ConditionVariable.cpp
56
Once.cpp
67
LinuxUlock.cpp
78
Fatal.cpp
+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
//===--- ConditionVariable.cpp - ConditionVariable Tests ------------------===//
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+
#include "swift/Threading/ConditionVariable.h"
14+
#include "gtest/gtest.h"
15+
16+
#include <chrono>
17+
#include <vector>
18+
19+
#include "ThreadingHelpers.h"
20+
#include "LockingHelpers.h"
21+
22+
using namespace swift;
23+
24+
template <typename Body>
25+
std::chrono::duration<double> measureDuration(Body body) {
26+
auto start = std::chrono::steady_clock::now();
27+
body();
28+
return std::chrono::steady_clock::now() - start;
29+
}
30+
31+
// -----------------------------------------------------------------------------
32+
33+
// Check we can lock, unlock, lock and unlock an unlocked condition variable
34+
TEST(ConditionVariableTest, BasicLockable) {
35+
ConditionVariable cond;
36+
basicLockable(cond);
37+
}
38+
39+
// Test ScopedLock and ScopedUnlock
40+
TEST(ConditionVariableTest, ScopedLockThreaded) {
41+
ConditionVariable cond;
42+
scopedLockThreaded<ConditionVariable::ScopedLock>(cond);
43+
}
44+
45+
TEST(ConditionVariableTest, ScopedUnlockUnderScopedLockThreaded) {
46+
ConditionVariable cond;
47+
scopedUnlockUnderScopedLockThreaded<ConditionVariable::ScopedLock,
48+
ConditionVariable::ScopedUnlock>(cond);
49+
}
50+
51+
// Test withLock()
52+
TEST(ConditionVariableTest, CriticalSectionThreaded) {
53+
ConditionVariable cond;
54+
criticalSectionThreaded(cond);
55+
}
56+
57+
// Check that timeouts work
58+
TEST(ConditionVariableTest, Timeout) {
59+
using namespace std::chrono_literals;
60+
61+
ConditionVariable cond;
62+
63+
auto duration = measureDuration([&] {
64+
ConditionVariable::ScopedLock lock(cond);
65+
bool ret = cond.wait(0.01s);
66+
ASSERT_FALSE(ret);
67+
});
68+
69+
ASSERT_GE(duration.count(), 0.01);
70+
71+
duration = measureDuration([&] {
72+
ConditionVariable::ScopedLock lock(cond);
73+
bool ret = cond.wait(0.1s);
74+
ASSERT_FALSE(ret);
75+
});
76+
77+
ASSERT_GE(duration.count(), 0.1);
78+
79+
duration = measureDuration([&] {
80+
ConditionVariable::ScopedLock lock(cond);
81+
bool ret = cond.wait(1s);
82+
ASSERT_FALSE(ret);
83+
});
84+
85+
ASSERT_GE(duration.count(), 1.0);
86+
87+
auto deadline = std::chrono::system_clock::now() + 0.5s;
88+
89+
duration = measureDuration([&] {
90+
ConditionVariable::ScopedLock lock(cond);
91+
bool ret = cond.waitUntil(deadline);
92+
ASSERT_FALSE(ret);
93+
});
94+
95+
ASSERT_GE(duration.count(), 0.5);
96+
}
97+
98+
#if !SWIFT_THREADING_NONE
99+
// Check that signal() wakes exactly one waiter
100+
TEST(ConditionVariableTest, Signal) {
101+
ConditionVariable cond;
102+
103+
int ready = 0, done = 0;
104+
std::vector<std::thread> threads;
105+
const int thread_count = 10;
106+
107+
for (int i = 0; i < thread_count; ++i) {
108+
threads.push_back(std::thread([&] {
109+
cond.lock();
110+
++ready;
111+
cond.wait();
112+
++done;
113+
cond.unlock();
114+
}));
115+
}
116+
117+
// Wait for all threads to be ready
118+
cond.withLock([&] {
119+
int tries = 1000;
120+
while (ready < thread_count && tries--) {
121+
ConditionVariable::ScopedUnlock unlock(cond);
122+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
123+
}
124+
});
125+
126+
ASSERT_EQ(ready, thread_count);
127+
128+
// Signal the condition variable and check that done increments by one
129+
// each time
130+
for (int i = 0; i < thread_count; ++i) {
131+
cond.signal();
132+
133+
cond.withLock([&] {
134+
int tries = 500;
135+
while (done == i && tries--) {
136+
ConditionVariable::ScopedUnlock unlock(cond);
137+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
138+
}
139+
});
140+
141+
ASSERT_EQ(done, i + 1);
142+
}
143+
144+
ASSERT_EQ(done, thread_count);
145+
146+
for (auto &thread : threads) {
147+
thread.join();
148+
}
149+
}
150+
151+
// Check that broadcast() wakes all waiters
152+
TEST(ConditionVariableTest, Broadcast) {
153+
ConditionVariable cond;
154+
155+
int ready = 0, done = 0;
156+
std::vector<std::thread> threads;
157+
const int thread_count = 10;
158+
159+
for (int i = 0; i < thread_count; ++i) {
160+
threads.push_back(std::thread([&] {
161+
cond.lock();
162+
++ready;
163+
cond.wait();
164+
++done;
165+
cond.unlock();
166+
}));
167+
}
168+
169+
// Wait for all threads to be ready
170+
cond.withLock([&] {
171+
int tries = 1000;
172+
while (ready < thread_count && tries--) {
173+
ConditionVariable::ScopedUnlock unlock(cond);
174+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
175+
}
176+
});
177+
178+
ASSERT_EQ(ready, thread_count);
179+
180+
// Broadcast and wait
181+
cond.broadcast();
182+
183+
cond.withLock([&] {
184+
int tries = 1000;
185+
while (done < thread_count && tries--) {
186+
ConditionVariable::ScopedUnlock unlock(cond);
187+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
188+
}
189+
});
190+
191+
ASSERT_EQ(done, thread_count);
192+
193+
for (auto &thread : threads) {
194+
thread.join();
195+
}
196+
}
197+
198+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.