Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Threading.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Common/CompilerMacrosExport.h"
5#ifndef SC_EXPORT_LIBRARY_THREADING
6#define SC_EXPORT_LIBRARY_THREADING 0
7#endif
8#define SC_THREADING_EXPORT SC_COMPILER_LIBRARY_EXPORT(SC_EXPORT_LIBRARY_THREADING)
9
10#include "../Common/AlignedStorage.h"
11#include "../Common/Assert.h"
12#include "../Common/Function.h"
13#include "../Common/PlatformMacrosType.h"
14#include "../Common/PrimitiveDefinitions.h"
15#include "../Common/Result.h"
16#include "Internal/Optional.h" // UniqueOptional
17
18namespace SC
19{
20SC_DECLARE_ASSERT_PROVIDER(ThreadingAssert, SC_THREADING_EXPORT);
21
22#define SC_THREADING_ASSERT_RELEASE(e) SC_ASSERT_PROVIDER_RELEASE(SC::ThreadingAssert, e)
23#define SC_THREADING_ASSERT_DEBUG(e) SC_ASSERT_PROVIDER_DEBUG(SC::ThreadingAssert, e)
24#define SC_THREADING_TRUST_RESULT(expression) SC_THREADING_ASSERT_RELEASE(expression)
25
28
31
36struct SC_THREADING_EXPORT Mutex
37{
38 Mutex();
39 ~Mutex();
40
41 // Underlying OS primitives can't be copied or moved
42 Mutex(const Mutex&) = delete;
43 Mutex(Mutex&&) = delete;
44 Mutex& operator=(const Mutex&) = delete;
45 Mutex& operator=(Mutex&&) = delete;
46
47 void lock();
48 void unlock();
49
50 private:
51 friend struct ConditionVariable;
52#if SC_PLATFORM_APPLE
53 static constexpr int OpaqueMutexSize = 56 + sizeof(long);
54 static constexpr int OpaqueMutexAlignment = alignof(long);
55#elif SC_PLATFORM_WINDOWS
56 static constexpr int OpaqueMutexSize = 4 * sizeof(void*) + 2 * sizeof(long);
57 static constexpr int OpaqueMutexAlignment = alignof(void*);
58#elif SC_PLATFORM_EMSCRIPTEN
59 static constexpr int OpaqueMutexSize = sizeof(void*) * 6 + sizeof(long);
60 static constexpr int OpaqueMutexAlignment = alignof(long);
61#else
62 static constexpr int OpaqueMutexSize = sizeof(void*) * 6;
63 static constexpr int OpaqueMutexAlignment = alignof(long);
64#endif
65 // Wrap native mutex as opaque array of bytes
66 using OpaqueMutex = AlignedStorage<OpaqueMutexSize, OpaqueMutexAlignment>;
67 OpaqueMutex mutex;
68};
69
71struct SC_THREADING_EXPORT ConditionVariable
72{
75
76 // Underlying OS primitives can't be copied or moved
77 ConditionVariable(const ConditionVariable&) = delete;
79 ConditionVariable& operator=(const ConditionVariable&) = delete;
80 ConditionVariable& operator=(ConditionVariable&&) = delete;
81
82 void wait(Mutex& mutex);
83 void signal();
84 void broadcast();
85
86 private:
87#if SC_PLATFORM_APPLE
88 static constexpr int OpaqueCVSize = 40 + sizeof(long);
89 static constexpr int OpaqueCVAlignment = alignof(long);
90#elif SC_PLATFORM_WINDOWS
91 static constexpr int OpaqueCVSize = sizeof(void*);
92 static constexpr int OpaqueCVAlignment = alignof(void*);
93#elif SC_PLATFORM_EMSCRIPTEN
94 static constexpr int OpaqueCVSize = sizeof(void*) * 12;
95 static constexpr int OpaqueCVAlignment = alignof(long);
96#else
97 static constexpr int OpaqueCVSize = sizeof(void*) * 6;
98 static constexpr int OpaqueCVAlignment = alignof(long);
99#endif
100
101 // Wrap native condition variable as opaque array of bytes
102 using OpaqueConditionVariable = AlignedStorage<OpaqueCVSize, OpaqueCVAlignment>;
103 OpaqueConditionVariable condition;
104};
105
126struct SC_THREADING_EXPORT Thread
127{
128 Thread() = default;
129 ~Thread();
130
131 // Cannot be copied or moved (as it would require a dynamic allocation for the type erased Function)
132 Thread(Thread&&) = delete;
133 Thread& operator=(Thread&&) = delete;
134 Thread(const Thread&) = delete;
135 Thread& operator=(const Thread&) = delete;
136
139 static uint64_t CurrentThreadID();
140
142 uint64_t threadID();
143
146 Result start(Function<void(Thread&)>&& func);
147
151 void setThreadName(const native_char_t* name);
152
155 Result join();
156
160 Result detach();
161
164 [[nodiscard]] bool wasStarted() const;
165
168 static void Sleep(uint32_t milliseconds);
169
170 private:
171 void setThreadNameInternal(const native_char_t* name);
172 struct Internal;
173 using OpaqueThread = AlignedStorage<sizeof(void*), alignof(void*)>;
174 UniqueOptional<OpaqueThread> thread;
175 Function<void(Thread&)> userFunction;
176};
177
182struct SC_THREADING_EXPORT RWLock
183{
184 RWLock() = default;
185 ~RWLock() = default;
186
187 RWLock(const RWLock&) = delete;
188 RWLock(RWLock&&) = delete;
189 RWLock& operator=(const RWLock&) = delete;
190 RWLock& operator=(RWLock&&) = delete;
191
193 void lockRead();
194
197
199 void lockWrite();
200
203
204 private:
205 Mutex mutex;
206
207 ConditionVariable readersQ;
208 ConditionVariable writersQ;
209
210 int activeReaders = 0;
211 int waitingWriters = 0;
212 bool writerActive = false;
213};
214
219struct SC_THREADING_EXPORT Barrier
220{
223 Barrier(uint32_t count) : threadCount(count) {}
224
226 void wait();
227
228 private:
229 const uint32_t threadCount;
230
231 uint32_t waitCount = 0;
232 uint32_t generation = 0;
233 Mutex mutex;
234
235 ConditionVariable condition;
236};
237
242struct SC_THREADING_EXPORT EventObject
243{
244 bool autoReset = true;
245
247 void wait();
248
250 void signal();
251
252 private:
253 bool isSignaled = false;
254 Mutex mutex;
255
257};
258
263struct SC_THREADING_EXPORT Semaphore
264{
267 Semaphore(int initialCount = 0);
268
270 void acquire();
271
273 void release();
274
275 private:
276 int count;
277 Mutex mutex;
278
279 ConditionVariable condition;
280};
281
283} // namespace SC
A synchronization point that blocks threads until the required number of threads have reached it.
Definition Threading.h:220
Barrier(uint32_t count)
Creates a barrier that waits for the specified number of threads.
Definition Threading.h:223
void wait()
Wait at the barrier until all threads have reached it.
A native OS condition variable.
Definition Threading.h:72
An automatically reset event object to synchronize two threads.
Definition Threading.h:243
void signal()
Unblocks another thread, waiting on EventObject::wait.
void wait()
Waits on a thread for EventObject::signal to be called from another thread.
A native OS mutex to synchronize access to shared resources.
Definition Threading.h:37
A Read-Write lock that allows multiple concurrent readers but only one writer.
Definition Threading.h:183
void lockWrite()
Acquire a write lock. Only one writer can hold the lock, and no readers can hold it simultaneously.
void unlockWrite()
Release a previously acquired write lock.
void lockRead()
Acquire a read lock. Multiple readers can hold the lock concurrently.
void unlockRead()
Release a previously acquired read lock.
A semaphore synchronization primitive that maintains a count for resource management.
Definition Threading.h:264
void acquire()
Wait for a resource to become available.
void release()
Make a resource available.
Semaphore(int initialCount=0)
Creates a semaphore with an initial count.
A native OS thread.
Definition Threading.h:127
bool wasStarted() const
Check if thread has been started.
static void Sleep(uint32_t milliseconds)
Puts current thread to sleep.
Result start(Function< void(Thread &)> &&func)
Starts the new thread with given name and func.
void setThreadName(const native_char_t *name)
Sets current thread name ONLY if called from inside the thread.
Result detach()
Detaches the thread so that its resources are automatically released back to the system without Threa...
static uint64_t CurrentThreadID()
Returns thread id of the thread calling the function.
uint64_t threadID()
Returns thread id of this thread object (not current thread)
Result join()
Waits for thread to finish and releases its resources.