Skip to content

Commit 1a74e0d

Browse files
Merge pull request #353 from insertinterestingnamehere/threadpool
New Internal Threadpool Implementation
2 parents 3349459 + 3b63359 commit 1a74e0d

13 files changed

+824
-1
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ jobs:
229229
export REPO_HTTPS=`echo "$CIRCLE_REPOSITORY_URL" | sed "s|[email protected]:|https://github.com/|g"`
230230
git clone -b "$CIRCLE_BRANCH" "$REPO_HTTPS" . --depth=1
231231
- run: |
232-
apk add --no-cache --no-progress bash make musl-dev hwloc-dev cmake gcc g++
232+
apk add --no-cache --no-progress bash make musl-dev hwloc-dev cmake gcc g++ linux-headers
233233
if [ "<< parameters.compiler >>" == "clang" ]; then apk add clang; fi
234234
- run: |
235235
if [ "<< parameters.compiler >>" == "clang" ]; then export CC=clang && export CXX=clang++; fi

include/qt_arithmetic.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef QT_ARITHMETIC_H
2+
#define QT_ARITHMETIC_H
3+
4+
// Miscellanious aritmetic utility macros.
5+
#define QTHREAD_MAX(a, b) (((a) > (b)) ? (a) : (b))
6+
#define QTHREAD_MIN(a, b) (((a) < (b)) ? (a) : (b))
7+
8+
#endif

include/qt_asserts.h

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
#include <assert.h> /* for assert() */
88

9+
#if __STDC_VERSION__ < 202311L
10+
#define qt_static_assert _Static_assert
11+
#else
12+
#define qt_static_assert static_assert
13+
#endif
14+
915
#ifdef qassert
1016
#undef qassert
1117
#endif

include/qt_atomic_wait.h

+282
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
#ifndef QT_ATOMIC_WAIT_H
2+
#define QT_ATOMIC_WAIT_H
3+
4+
#include <assert.h>
5+
#include <stdatomic.h>
6+
#include <stdint.h>
7+
8+
#include "qt_asserts.h"
9+
#include "qt_os.h"
10+
11+
// This header defines a cross-platform futex-like API.
12+
// It's somewhat like atomic_wait in c++20, however these constructs are
13+
// guaranteed to use the appropriate OS APIs directly. It's also somewhat like
14+
// Rust's https://github.com/m-ou-se/atomic-wait, except this is just a bunch of
15+
// C macros and it supports a few more OSs.
16+
17+
// Linux only has 32-bit futexes so that's the only size that's possible to
18+
// standardize.
19+
#define qt_atomic_wait_t _Atomic uint32_t
20+
#define qt_atomic_wait_empty 0u
21+
#define qt_atomic_wait_full UINT32_MAX
22+
#define qt_atomic_wait_set_empty(a) \
23+
atomic_store_explicit((a), 0u, memory_order_relaxed)
24+
#define qt_atomic_wait_set_full(a) \
25+
atomic_store_explicit((a), UINT32_MAX, memory_order_relaxed)
26+
#define qt_atomic_wait_load(a) atomic_load_explicit((a), memory_order_relaxed)
27+
#define qt_atomic_wait_store(a, v) \
28+
atomic_store_explicit((a), v, memory_order_relaxed)
29+
30+
// Futex-like atomic wait functionality that's guaranteed to use
31+
// the appropriate OS thread pausing functionality (e.g. futex).
32+
// Due to constraints between the various operating systems,
33+
// only 32-bit integers are supported.
34+
35+
#ifdef QTHREADS_LINUX
36+
37+
// Use Linux futexes
38+
39+
#include <errno.h>
40+
#include <linux/futex.h>
41+
#include <sys/syscall.h>
42+
#include <unistd.h>
43+
44+
#ifndef NDEBUG
45+
#define qt_wait_on_address(a, expected) \
46+
do { \
47+
long status = syscall(SYS_futex, \
48+
(a), \
49+
FUTEX_WAIT | FUTEX_PRIVATE_FLAG, \
50+
(expected), \
51+
NULL, \
52+
NULL, \
53+
0u); \
54+
/* EAGAIN means the value already changed so no sleep was necessary. */ \
55+
assert(!status || (status == -1 && errno == EAGAIN)); \
56+
} while (0)
57+
#else
58+
#define qt_wait_on_address(a, expected) \
59+
do { \
60+
syscall(SYS_futex, \
61+
(a), \
62+
FUTEX_WAIT | FUTEX_PRIVATE_FLAG, \
63+
(expected), \
64+
NULL, \
65+
NULL, \
66+
0u); \
67+
} while (0)
68+
#endif
69+
70+
#define qt_wake_all(a) \
71+
do { \
72+
syscall(SYS_futex, \
73+
(a), \
74+
FUTEX_WAKE | FUTEX_PRIVATE_FLAG, \
75+
UINT32_MAX, \
76+
NULL, \
77+
NULL, \
78+
0u); \
79+
} while (0)
80+
81+
#define qt_wake_one(a) \
82+
do { \
83+
syscall( \
84+
SYS_futex, (a), FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1u, NULL, NULL, 0u); \
85+
} while (0)
86+
87+
#elif defined(QTHREADS_APPLE)
88+
// use __ulock_wait and __ulock_wake
89+
// NOTE! This isn't technically a stable API even though they export the
90+
// symbols, so watch for changes in OSX releases later. libc++ relies on them
91+
// though so changes seem very unlikely.
92+
93+
// Can't directly include apple's sys/ulock.h but we only need these symbols.
94+
// They are exported from libSystem though, which also provides their libc.
95+
// Given that, presumably we're already linking to them.
96+
// See
97+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/ulock.h#L64-L68
98+
extern int
99+
__ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout);
100+
extern int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
101+
102+
// Corresponding operation codes:
103+
// See
104+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/ulock.h#L72-L137
105+
#define UL_COMPARE_AND_WAIT 1
106+
#define ULF_WAKE_ALL 0x00000100
107+
#define ULF_NO_ERRNO 0x01000000
108+
109+
// Note: no need to check for the case where a wait operation
110+
// wakes because the flag was already changed.
111+
// In that case the return value ends up being the same.
112+
// See
113+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/kern/waitq.c#L2819-L2820
114+
// See also
115+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/kern/sys_ulock.c#L424
116+
#define qt_wait_on_address(a, expected) \
117+
do { \
118+
qassert( \
119+
__ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, (a), (expected), 0ull), \
120+
0); \
121+
} while (0)
122+
123+
#define qt_wake_all(a) \
124+
do { \
125+
qassert(__ulock_wake(UL_COMPARE_AND_WAIT | ULF_WAKE_ALL | ULF_NO_ERRNO, \
126+
(a), \
127+
0ull) >= 0, \
128+
1); \
129+
} while (0)
130+
131+
#define qt_wake_one(a) \
132+
do { __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, (a), 0ull); } while (0)
133+
134+
#elif defined(QTHREADS_FREEBSD)
135+
// use _umtx_op
136+
#include <sys/types.h>
137+
#include <sys/umtx.h>
138+
139+
#define qt_wait_on_address(a, expected) \
140+
do { \
141+
qassert(_umtx_op((a), UMTX_OP_WAIT_UINT_PRIVATE, (expected), NULL, NULL), \
142+
0); \
143+
} while (0)
144+
145+
#define qt_wake_all(a) \
146+
do { \
147+
/* Docs say use INT32_MAX to specify all. */ \
148+
qassert( \
149+
_umtx_op( \
150+
(a), UMTX_OP_WAKE_PRIVATE, (unsigned long)INT32_MAX, NULL, NULL), \
151+
0); \
152+
} while (0)
153+
154+
#define qt_wake_one(a) \
155+
do { \
156+
qassert(_umtx_op((a), UMTX_OP_WAKE_PRIVATE, 1ul, NULL, NULL), 0); \
157+
} while (0)
158+
159+
#elif defined(QTHREADS_OPENBSD)
160+
// use futex syscall wrapper they provide: https://man.openbsd.org/futex
161+
#include <errno.h>
162+
#include <sys/futex.h>
163+
#include <sys/time.h>
164+
165+
#ifndef NDEBUG
166+
#define qt_wait_on_address(a, expected) \
167+
do { \
168+
int status = \
169+
futex((a), FUTEX_WAIT | FUTEX_PRIVATE_FLAG, (expected), NULL, NULL); \
170+
assert(!status || (status == -1 && errno == EAGAIN));
171+
}
172+
while (0)
173+
#else
174+
#define qt_wait_on_address(a, expected) \
175+
do { \
176+
futex((a), FUTEX_WAIT | FUTEX_PRIVATE_FLAG, (expected), NULL, NULL); \
177+
} while (0)
178+
#endif
179+
180+
#define qt_wake_all(a) \
181+
do { \
182+
/* For whatever reason they used a signed integer for the val parameter so \
183+
* use INT32_MAX.*/ \
184+
futex((a), FUTEX_WAKE | FUTEX_PRIVATE_FLAG, INT32_MAX, NULL, NULL); \
185+
} while (0)
186+
187+
#define qt_wake_one(a) \
188+
do { futex((a), FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL); } while (0)
189+
190+
#elif defined(QTHREADS_NETBSD)
191+
// use SYS___futex syscall
192+
#include <errno.h>
193+
#include <sys/futex.h>
194+
#include <sys/syscall.h>
195+
#include <sys/time.h>
196+
197+
#ifndef NDEBUG
198+
#define qt_wait_on_address(a, expected) \
199+
do { \
200+
int status = syscall(SYS___futex, \
201+
(a), \
202+
FUTEX_WAIT | FUTEX_PRIVATE_FLAG, \
203+
(expected), \
204+
NULL, \
205+
NULL); \
206+
assert(!status || status == -1 && errno == EAGAIN); \
207+
} while (0)
208+
#else
209+
#define qt_wait_on_address(a, expected) \
210+
do { \
211+
syscall(SYS___futex, \
212+
(a), \
213+
FUTEX_WAIT | FUTEX_PRIVATE_FLAG, \
214+
(expected), \
215+
NULL, \
216+
NULL); \
217+
while (0)
218+
#endif
219+
220+
#define qt_wake_all(a) \
221+
do { \
222+
syscall(SYS___futex, \
223+
(a), \
224+
FUTEX_WAKE | FUTEX_PRIVATE_FLAG, \
225+
UINT32_MAX, \
226+
NULL, \
227+
NULL); \
228+
} while (0)
229+
230+
#define qt_wake_one(a) \
231+
do { \
232+
syscall( \
233+
SYS___futex, (a), FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1u, NULL, NULL); \
234+
} while (0)
235+
236+
#elif defined(QTHREADS_DRAGONFLYBSD)
237+
// use umtx_sleep and umtx_wakeup
238+
#include <errno.h>
239+
#include <unistd.h>
240+
#ifndef NDEBUG
241+
#define qt_wait_on_address(a, expected) \
242+
do { \
243+
int status = umtx_sleep((a), (expected), 0); \
244+
assert(!status || (status == -1 && errno == EBUSY)); \
245+
} while (0)
246+
#else
247+
#define qt_wait_on_address(a, expected) \
248+
do { umtx_sleep((a), (expected), 0); } while (0)
249+
#endif
250+
251+
#define qt_wake_all(a) \
252+
do { \
253+
/* The interface uses signed integers, so use INT32_MAX here */ \
254+
qassert(umtx_wakeup((a), INT32_MAX), 0); \
255+
} while (0)
256+
257+
#define qt_wake_one(a) \
258+
do { qassert(umtx_wakeup((a), 1), 0); } while (0)
259+
260+
#elif defined(QTHREADS_WINDOWS)
261+
// use WaitOnAddress/WakeByAddressSingle/WakeByAddressAll
262+
#include <synchapi.h>
263+
#define qt_wait_on_address(a, expected) \
264+
do { qassert(WaitOnAddress((a), (expected), 4, INFINITE), TRUE); } while (0)
265+
266+
#define qt_wake_all(a) \
267+
do { WakeByAddressAll(a); } while (0)
268+
269+
#define qt_wake_one(a) \
270+
do { WakeByAddressSingle(a); } while (0)
271+
272+
#elif defined(__sun)
273+
// Solaris supposedly provides something futex-like via "user-level adaptive
274+
// spin mutexes".
275+
// TODO: implement that
276+
#error "futex equivalent not implemented for solaris."
277+
278+
#else
279+
#error "no known futex equivalent for current OS"
280+
#endif
281+
282+
#endif

include/qt_branching.h

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef QT_BRANCHING_H
2+
#define QT_BRANCHING_H
3+
4+
#define likely(x) (__builtin_expect(!!(x), 1))
5+
#define unlikely(x) (__builtin_expect(!!(x), 0))
6+
7+
#endif

include/qt_macros.h

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
#define QTHREAD_MSAN
2424
#endif
2525
#endif
26+
#ifdef QTHREAD_MSAN
27+
#define QTHREAD_SUPPRESS_MSAN __attribute__((no_sanitize("memory")))
28+
#else
29+
#define QTHREAD_SUPPRESS_MSAN
30+
#endif
2631

2732
#define TLS_DECL(type, name) thread_local type name
2833
#define TLS_DECL_INIT(type, name) thread_local type name = 0

include/qt_os.h

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#ifndef QT_OS_H
2+
#define QT_OS_H
3+
4+
// Not all Operating systems listed here are actually supported.
5+
6+
#if defined(__linux__) || defined(QTHREADS_LINUX)
7+
#ifndef QTHREADS_LINUX
8+
#define QTHREADS_LINUX
9+
#endif
10+
11+
#elif defined(__APPLE__) || defined(QTHREADS_APPLE)
12+
#ifndef QTHREADS_APPLE
13+
#define QTHREADS_APPLE
14+
#endif
15+
16+
#elif defined(_WIN32) || defined(QTHREADS_WINDOWS)
17+
#ifndef QTHREADS_WINDOWS
18+
#define QTHREADS_WINDOWS
19+
#endif
20+
21+
#elif defined(__FreeBSD__) || defined(QTHREADS_FREEBSD)
22+
#ifndef QTHREADS_FREEBSD
23+
#define QTHREADS_FREEBSD
24+
#endif
25+
26+
#elif defined(__NetBSD__) || defined(QTHREADS_NETBSD)
27+
#ifndef QTHREADS_NETBSD
28+
#define QTHREADS_NETBSD
29+
#endif
30+
31+
#elif defined(__OpenBSD__) || defined(QTHREADS_OPENBSD)
32+
#ifndef QTHREADS_OPENBSD
33+
#define QTHREADS_OPENBSD
34+
#endif
35+
36+
#elif defined(__DragonFly__) || defined(QTHREADS_DRAGONFLYBSD)
37+
#ifndef QTHREADS_DRAGONFLYBSD
38+
#define QTHREADS_DRAGONFLYBSD
39+
#endif
40+
41+
#elif defined(__sun)
42+
#error "Sun OS not currently supported."
43+
44+
#else
45+
#error "Unrecognized OS"
46+
47+
#endif
48+
49+
#endif

0 commit comments

Comments
 (0)