[Threading] Add ConditionVariable support.

Swift Concurrency would like to be able to use condition variables.
Add support to the threading packages.

rdar://100236038
This commit is contained in:
Alastair Houghton
2022-09-23 14:45:03 +01:00
parent b1040649f2
commit 4495d63c12
12 changed files with 766 additions and 50 deletions

View File

@@ -18,6 +18,7 @@
#define SWIFT_THREADING_IMPL_C11_H
#include <atomic>
#include <chrono>
#include <cstdint>
#include <threads.h>
@@ -36,13 +37,13 @@ namespace threading_impl {
swift::threading::fatal(#expr " failed with error %d\n", res_); \
} while (0)
#define SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(expr) \
#define SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
do { \
int res_ = (expr); \
switch (res_) { \
case thrd_success: \
return true; \
case thrd_busy: \
case falseerr: \
return false; \
default: \
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
@@ -78,7 +79,7 @@ inline void mutex_unlock(mutex_handle &handle) {
SWIFT_C11THREADS_CHECK(::mtx_unlock(&handle));
}
inline bool mutex_try_lock(mutex_handle &handle) {
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(::mtx_trylock(&handle));
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(thrd_busy, ::mtx_trylock(&handle));
}
inline void mutex_unsafe_lock(mutex_handle &handle) {
@@ -134,7 +135,7 @@ inline void lazy_mutex_unlock(lazy_mutex_handle &handle) {
}
inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) {
lazy_mutex_init(handle);
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(::mtx_trylock(&handle.mutex));
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(thrd_busy, ::mtx_trylock(&handle.mutex));
}
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {
@@ -146,6 +147,53 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
(void)::mtx_unlock(&handle.mutex);
}
// .. ConditionVariable support ..............................................
struct cond_handle {
::cnd_t condition;
::mtx_t mutex;
};
inline void cond_init(cond_handle &handle) {
SWIFT_C11THREADS_CHECK(::cnd_init(&handle.condition));
SWIFT_C11THREADS_CHECK(::mtx_init(&handle.mutex, ::mtx_plain));
}
inline void cond_destroy(cond_handle &handle) {
::cnd_destroy(&handle.condition);
::mtx_destroy(&handle.mutex);
}
inline void cond_lock(cond_handle &handle) {
SWIFT_C11THREADS_CHECK(::mtx_lock(&handle.mutex));
}
inline void cond_unlock(cond_handle &handle) {
SWIFT_C11THREADS_CHECK(::mtx_unlock(&handle.mutex));
}
inline void cond_signal(cond_handle &handle) {
SWIFT_C11THREADS_CHECK(::cnd_signal(&handle.condition));
}
inline void cond_broadcast(cond_handle &handle) {
SWIFT_C11THREADS_CHECK(::cnd_broadcast(&handle.condition));
}
inline void cond_wait(cond_handle &handle) {
SWIFT_C11THREADS_CHECK(::cnd_wait(&handle.condition, &handle.mutex));
}
template <class Rep, class Period>
inline bool cond_wait(cond_handle &handle,
std::chrono::duration<Rep, Period> duration) {
auto deadline = std::chrono::system_clock::now() + duration;
return cond_wait(handle, deadline);
}
inline bool cond_wait(cond_handle &handle,
std::chrono::system_clock::time_point deadline) {
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
deadline.time_since_epoch()).count();
struct ::timespec ts = { ::time_t(ns / 1000000000), long(ns % 1000000000) };
SWIFT_C11THREADS_RETURN_TRUE_OR_FALSE(
thrd_timedout,
::cnd_timedwait(&handle.condition, &handle.mutex, &ts)
);
}
// .. Once ...................................................................
typedef std::atomic<std::intptr_t> once_t;

View File

@@ -21,6 +21,8 @@
#include <os/lock.h>
#include <pthread.h>
#include <chrono>
#include "llvm/ADT/Optional.h"
#include "swift/Threading/Errors.h"
@@ -28,6 +30,26 @@
namespace swift {
namespace threading_impl {
#define SWIFT_PTHREADS_CHECK(expr) \
do { \
int res_ = (expr); \
if (res_ != 0) \
swift::threading::fatal(#expr " failed with error %d\n", res_); \
} while (0)
#define SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
do { \
int res_ = (expr); \
switch (res_) { \
case 0: \
return true; \
case falseerr: \
return false; \
default: \
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
} \
} while (0)
// .. Thread related things ..................................................
using thread_id = ::pthread_t;
@@ -102,6 +124,55 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
::os_unfair_lock_unlock(&handle);
}
// .. ConditionVariable support ..............................................
struct cond_handle {
::pthread_cond_t condition;
::pthread_mutex_t mutex;
};
inline void cond_init(cond_handle &handle) {
handle.condition = PTHREAD_COND_INITIALIZER;
handle.mutex = PTHREAD_MUTEX_INITIALIZER;
}
inline void cond_destroy(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_destroy(&handle.condition));
SWIFT_PTHREADS_CHECK(::pthread_mutex_destroy(&handle.mutex));
}
inline void cond_lock(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_mutex_lock(&handle.mutex));
}
inline void cond_unlock(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_mutex_unlock(&handle.mutex));
}
inline void cond_signal(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_signal(&handle.condition));
}
inline void cond_broadcast(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_broadcast(&handle.condition));
}
inline void cond_wait(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_wait(&handle.condition, &handle.mutex));
}
template <class Rep, class Period>
inline bool cond_wait(cond_handle &handle,
std::chrono::duration<Rep, Period> duration) {
auto deadline = std::chrono::time_point_cast<
std::chrono::system_clock::duration>(std::chrono::system_clock::now()
+ duration);
return cond_wait(handle, deadline);
}
inline bool cond_wait(cond_handle &handle,
std::chrono::system_clock::time_point deadline) {
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
deadline.time_since_epoch()).count();
struct ::timespec ts = { ::time_t(ns / 1000000000), long(ns % 1000000000) };
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(
ETIMEDOUT,
::pthread_cond_timedwait(&handle.condition, &handle.mutex, &ts)
);
}
// .. Once ...................................................................
using once_t = ::dispatch_once_t;

View File

@@ -1,4 +1,4 @@
//==--- Pthreads.h - Threading abstraction implementation ------ -*-C++ -*-===//
//==--- Linux.h - Threading abstraction implementation --------- -*-C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
@@ -21,6 +21,7 @@
#include <pthread.h>
#include <atomic>
#include <chrono>
#include <optional>
#include "llvm/ADT/Optional.h"
@@ -39,13 +40,13 @@ namespace threading_impl {
swift::threading::fatal(#expr " failed with error %d\n", res_); \
} while (0)
#define SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(expr) \
#define SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
do { \
int res_ = (expr); \
switch (res_) { \
case 0: \
return true; \
case EBUSY: \
case falseerr: \
return false; \
default: \
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
@@ -93,7 +94,8 @@ inline void mutex_unlock(mutex_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_unlock(&handle));
}
inline bool mutex_try_lock(mutex_handle &handle) {
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(EBUSY,
::pthread_mutex_trylock(&handle));
}
inline void mutex_unsafe_lock(mutex_handle &handle) {
@@ -121,7 +123,8 @@ inline void lazy_mutex_unlock(lazy_mutex_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_unlock(&handle));
}
inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) {
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(EBUSY,
::pthread_mutex_trylock(&handle));
}
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {
@@ -131,6 +134,55 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
(void)::pthread_mutex_unlock(&handle);
}
// .. ConditionVariable support ..............................................
struct cond_handle {
::pthread_cond_t condition;
::pthread_mutex_t mutex;
};
inline void cond_init(cond_handle &handle) {
handle.condition = PTHREAD_COND_INITIALIZER;
handle.mutex = PTHREAD_MUTEX_INITIALIZER;
}
inline void cond_destroy(cond_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_destroy(&handle.condition));
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_destroy(&handle.mutex));
}
inline void cond_lock(cond_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_lock(&handle.mutex));
}
inline void cond_unlock(cond_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_mutex_unlock(&handle.mutex));
}
inline void cond_signal(cond_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_signal(&handle.condition));
}
inline void cond_broadcast(cond_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_broadcast(&handle.condition));
}
inline void cond_wait(cond_handle &handle) {
SWIFT_LINUXTHREADS_CHECK(::pthread_cond_wait(&handle.condition, &handle.mutex));
}
template <class Rep, class Period>
inline bool cond_wait(cond_handle &handle,
std::chrono::duration<Rep, Period> duration) {
auto deadline = std::chrono::time_point_cast<
std::chrono::system_clock::duration>(std::chrono::system_clock::now()
+ duration);
return cond_wait(handle, deadline);
}
inline bool cond_wait(cond_handle &handle,
std::chrono::system_clock::time_point deadline) {
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
deadline.time_since_epoch()).count();
struct ::timespec ts = { ::time_t(ns / 1000000000), long(ns % 1000000000) };
SWIFT_LINUXTHREADS_RETURN_TRUE_OR_FALSE(
ETIMEDOUT,
::pthread_cond_timedwait(&handle.condition, &handle.mutex, &ts)
);
}
// .. Once ...................................................................
struct once_t {

View File

@@ -19,6 +19,8 @@
#include "llvm/ADT/Optional.h"
#include <chrono>
namespace swift {
namespace threading_impl {
@@ -57,6 +59,27 @@ inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) { return true; }
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {}
inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {}
// .. ConditionVariable support ..............................................
using cond_handle = unsigned;
inline void cond_init(cond_handle &handle) {}
inline void cond_destroy(cond_handle &handle) {}
inline void cond_lock(cond_handle &handle) {}
inline void cond_unlock(cond_handle &handle) {}
inline void cond_signal(cond_handle &handle) {}
inline void cond_broadcast(cond_handle &handle) {}
inline void cond_wait(cond_handle &handle) {}
template <class Rep, class Period>
inline bool cond_wait(cond_handle &handle,
std::chrono::duration<Rep, Period> duration) {
return true;
}
inline bool cond_wait(cond_handle &handle,
std::chrono::system_clock::time_point deadline) {
return true;
}
// .. Once ...................................................................
typedef bool once_t;

View File

@@ -21,6 +21,7 @@
#include <pthread.h>
#include <atomic>
#include <chrono>
#include <cstdint>
#include "llvm/ADT/Optional.h"
@@ -37,13 +38,13 @@ namespace threading_impl {
swift::threading::fatal(#expr " failed with error %d\n", res_); \
} while (0)
#define SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(expr) \
#define SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(falseerr, expr) \
do { \
int res_ = (expr); \
switch (res_) { \
case 0: \
return true; \
case EBUSY: \
case falseerr: \
return false; \
default: \
swift::threading::fatal(#expr " failed with error (%d)\n", res_); \
@@ -91,7 +92,7 @@ inline void mutex_unlock(mutex_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_mutex_unlock(&handle));
}
inline bool mutex_try_lock(mutex_handle &handle) {
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(EBUSY, ::pthread_mutex_trylock(&handle));
}
inline void mutex_unsafe_lock(mutex_handle &handle) {
@@ -119,7 +120,7 @@ inline void lazy_mutex_unlock(lazy_mutex_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_mutex_unlock(&handle));
}
inline bool lazy_mutex_try_lock(lazy_mutex_handle &handle) {
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(::pthread_mutex_trylock(&handle));
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(EBUSY, ::pthread_mutex_trylock(&handle));
}
inline void lazy_mutex_unsafe_lock(lazy_mutex_handle &handle) {
@@ -129,6 +130,55 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
(void)::pthread_mutex_unlock(&handle);
}
// .. ConditionVariable support ..............................................
struct cond_handle {
::pthread_cond_t condition;
::pthread_mutex_t mutex;
};
inline void cond_init(cond_handle &handle) {
handle.condition = PTHREAD_COND_INITIALIZER;
handle.mutex = PTHREAD_MUTEX_INITIALIZER;
}
inline void cond_destroy(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_destroy(&handle.condition));
SWIFT_PTHREADS_CHECK(::pthread_mutex_destroy(&handle.mutex));
}
inline void cond_lock(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_mutex_lock(&handle.mutex));
}
inline void cond_unlock(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_mutex_unlock(&handle.mutex));
}
inline void cond_signal(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_signal(&handle.condition));
}
inline void cond_broadcast(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_broadcast(&handle.condition));
}
inline void cond_wait(cond_handle &handle) {
SWIFT_PTHREADS_CHECK(::pthread_cond_wait(&handle.condition, &handle.mutex));
}
template <class Rep, class Period>
inline bool cond_wait(cond_handle &handle,
std::chrono::duration<Rep, Period> duration) {
auto deadline = std::chrono::time_point_cast<
std::chrono::system_clock::duration>(std::chrono::system_clock::now()
+ duration);
return cond_wait(handle, deadline);
}
inline bool cond_wait(cond_handle &handle,
std::chrono::system_clock::time_point deadline) {
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
deadline.time_since_epoch()).count();
struct ::timespec ts = { ::time_t(ns / 1000000000), long(ns % 1000000000) };
SWIFT_PTHREADS_RETURN_TRUE_OR_FALSE(
ETIMEDOUT,
::pthread_cond_timedwait(&handle.condition, &handle.mutex, &ts)
);
}
// .. Once ...................................................................
using once_t = std::atomic<std::intptr_t>;

View File

@@ -19,6 +19,7 @@
#include "Win32/Win32Defs.h"
#include <chrono>
#include <atomic>
#include "llvm/ADT/Optional.h"
@@ -86,6 +87,54 @@ inline void lazy_mutex_unsafe_unlock(lazy_mutex_handle &handle) {
ReleaseSRWLockExclusive(&handle);
}
// .. ConditionVariable support ..............................................
struct cond_handle {
SWIFT_CONDITION_VARIABLE condition;
SWIFT_SRWLOCK lock;
};
inline void cond_init(cond_handle &handle) {
handle.condition = CONDITION_VARIABLE_INIT;
handle.lock = SRWLOCK_INIT;
}
inline void cond_destroy(cond_handle &handle) {}
inline void cond_lock(cond_handle &handle) {
AcquireSRWLockExclusive(&handle.lock);
}
inline void cond_unlock(cond_handle &handle) {
ReleaseSRWLockExclusive(&handle.lock);
}
inline void cond_signal(cond_handle &handle) {
WakeConditionVariable(&handle.condition);
}
inline void cond_broadcast(cond_handle &handle) {
WakeAllConditionVariable(&handle.condition);
}
inline void cond_wait(cond_handle &handle) {
SleepConditionVariableSRW(&handle.condition,
&handle.lock,
INFINITE,
0);
}
template <class Rep, class Period>
inline bool cond_wait(cond_handle &handle,
std::chrono::duration<Rep, Period> duration) {
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
return SleepConditionVariableSRW(&handle.condition,
&handle.lock,
DWORD(ms.count()),
0);
}
inline bool cond_wait(cond_handle &handle,
std::chrono::system_clock::time_point deadline) {
std::chrono::system_clock::duration duration =
deadline - std::chrono::system_clock::now();
if (duration < std::chrono::system_clock::duration::zero())
duration = std::chrono::system_clock::duration::zero();
return cond_wait(handle, duration);
}
// .. Once ...................................................................
typedef std::atomic<intptr_t> once_t;

View File

@@ -36,18 +36,30 @@ typedef unsigned char BYTE;
typedef BYTE BOOLEAN;
typedef int BOOL;
typedef unsigned long DWORD;
typedef unsigned long ULONG;
typedef VOID(NTAPI *PFLS_CALLBACK_FUNCTION)(PVOID lpFlsData);
typedef struct _RTL_SRWLOCK *PRTL_SRWLOCK;
typedef PRTL_SRWLOCK PSRWLOCK;
typedef struct _RTL_CONDITION_VARIABLE *PRTL_CONDITION_VARIABLE;
typedef PRTL_CONDITION_VARIABLE PCONDITION_VARIABLE;
// These have to be #defines, to avoid problems with <windows.h>
#define RTL_SRWLOCK_INIT \
{ 0 }
#define SRWLOCK_INIT RTL_SRWLOCK_INIT
#define FLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
#define RTL_CONDITION_VARIABLE_INIT {0}
#define CONDITION_VARIABLE_INIT RTL_CONDITION_VARIABLE_INIT
#define RTL_CONDITION_VARIABLE_LOCKMODE_SHARED 0x1
#define CONDITION_VARIABLE_LOCKMODE_SHARED RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
#define INFINITE 0xFFFFFFFF // Infinite timeout
extern "C" {
WINBASEAPI DWORD WINAPI GetCurrentThreadId(VOID);
@@ -56,6 +68,22 @@ WINBASEAPI VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
WINBASEAPI VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK SRWLock);
WINBASEAPI BOOLEAN WINAPI TryAcquireSRWLockExclusive(PSRWLOCK SRWLock);
WINBASEAPI VOID WINAPI InitializeConditionVariable(
PCONDITION_VARIABLE ConditionVariable
);
WINBASEAPI VOID WINAPI WakeConditionVariable(
PCONDITION_VARIABLE ConditionVariable
);
WINBASEAPI VOID WINAPI WakeAllConditionVariable(
PCONDITION_VARIABLE ConditionVariable
);
WINBASEAPI BOOL WINAPI SleepConditionVariableSRW(
PCONDITION_VARIABLE ConditionVariable,
PSRWLOCK SRWLock,
DWORD dwMilliseconds,
ULONG Flags
);
WINBASEAPI DWORD WINAPI FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback);
WINBASEAPI PVOID WINAPI FlsGetValue(DWORD dwFlsIndex);
WINBASEAPI BOOL WINAPI FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData);
@@ -86,6 +114,33 @@ inline BOOLEAN TryAcquireSRWLockExclusive(PSWIFT_SRWLOCK SRWLock) {
return ::TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(SRWLock));
}
// Similarly we have the same problem with _RTL_CONDITION_VARIABLE
struct SWIFT_CONDITION_VARIABLE {
PVOID Ptr;
};
typedef SWIFT_CONDITION_VARIABLE *PSWIFT_CONDITION_VARIABLE;
inline VOID InitializeConditionVariable(PSWIFT_CONDITION_VARIABLE CondVar) {
::InitializeConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(CondVar));
}
inline VOID WakeConditionVariable(PSWIFT_CONDITION_VARIABLE CondVar) {
::WakeConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(CondVar));
}
inline VOID WakeAllConditionVariable(PSWIFT_CONDITION_VARIABLE CondVar) {
::WakeAllConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(CondVar));
}
inline BOOL SleepConditionVariableSRW(PSWIFT_CONDITION_VARIABLE CondVar,
PSWIFT_SRWLOCK SRWLock,
DWORD dwMilliseconds,
ULONG Flags) {
return ::SleepConditionVariableSRW(
reinterpret_cast<PCONDITION_VARIABLE>(CondVar),
reinterpret_cast<PSRWLOCK>(SRWLock),
dwMilliseconds,
Flags);
}
} // namespace threading_impl
} // namespace swift