forked from GerHobbelt/pthread-win32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcthread.h
560 lines (493 loc) · 20.5 KB
/
cthread.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
#ifndef _CTHREAD_H_
#define _CTHREAD_H_
#if defined(_WIN32) || defined(_WIN64)
#if !defined(__cplusplus)
#define __STDC__ 1
#endif
#endif
#if defined(__TINYC__) || defined(USE_EMULATED_TLS)
#undef emulate_tls
#define emulate_tls 1
#ifndef thread_local
#define thread_local
#endif
#elif !defined(thread_local) /* User can override thread_local for obscure compilers */
/* Running in multi-threaded environment */
#if defined(__STDC__) /* Compiling as C Language */
#if defined(_MSC_VER) /* Don't rely on MSVC's C11 support */
#define thread_local __declspec(thread)
#elif __STDC_VERSION__ < 201112L /* If we are on C90/99 */
#if defined(__clang__) || defined(__GNUC__) /* Clang and GCC */
#define thread_local __thread
#else /* Otherwise, we ignore the directive (unless user provides their own) */
#define thread_local
#define emulate_tls 1
#endif
#elif __APPLE__ && __MACH__
#define thread_local __thread
#else /* C11 and newer define thread_local in threads.h */
#define HAS_C11_THREADS 1
#include <threads.h>
#endif
#elif defined(__cplusplus) /* Compiling as C++ Language */
#if __cplusplus < 201103L /* thread_local is a C++11 feature */
#if defined(_MSC_VER)
#define thread_local __declspec(thread)
#elif defined(__clang__) || defined(__GNUC__)
#define thread_local __thread
#else /* Otherwise, we ignore the directive (unless user provides their own) */
#define thread_local
#define emulate_tls 1
#endif
#else /* In C++ >= 11, thread_local in a builtin keyword */
/* Don't do anything */
#endif
#define HAS_C11_THREADS 1
#endif
#endif
#ifndef HAS_C11_THREADS
#ifdef __cplusplus
extern "C" {
#endif
/* Which platform are we on? */
#if !defined(_CTHREAD_PLATFORM_DEFINED_)
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
#define _CTHREAD_WIN32_
#else
#define _CTHREAD_POSIX_
#endif
#define _CTHREAD_PLATFORM_DEFINED_
#endif
/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
#if defined(_CTHREAD_POSIX_)
#undef _FEATURES_H
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
#define _XPG6
#endif
/* Generic includes */
#include <pthread.h>
/* Platform specific includes */
#if defined(_CTHREAD_POSIX_)
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <limits.h>
#elif defined(_CTHREAD_WIN32_)
#include <windows.h>
#include <sys/timeb.h>
# ifndef usleep
# define usleep(n) Sleep(n)
# endif
#endif
#include <time.h>
#include <stdbool.h>
/* Compiler-specific information */
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define CTHREAD_NORETURN _Noreturn
#elif defined(__GNUC__)
#define CTHREAD_NORETURN __attribute__((__noreturn__))
#else
#define CTHREAD_NORETURN
#endif
/* Function return values */
enum
{
thrd_success = 0, /**< The requested operation succeeded */
thrd_busy = 1, /**< The requested operation failed because a resource requested by a test and return function is already in use */
thrd_error = 2, /**< The requested operation failed */
thrd_nomem = 3, /**< The requested operation failed because it was unable to allocate memory */
thrd_timedout = 4 /**< The time specified in the call was reached without acquiring the requested resource */
};
/* Mutex types */
enum
{
mtx_plain = 0,
mtx_recursive = 1,
mtx_timed = 2
};
/* Mutex */
typedef pthread_mutex_t mtx_t;
/** Create a mutex object.
* @param mtx A mutex object.
* @param type Bit-mask that must have one of the following six values:
* @li @c mtx_plain for a simple non-recursive mutex
* @li @c mtx_timed for a non-recursive mutex that supports timeout
* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_init(mtx_t *mtx, int type);
/** Release any resources used by the given mutex.
* @param mtx A mutex object.
*/
void mtx_destroy(mtx_t *mtx);
/** Lock the given mutex.
* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
* the calling thread already has a lock on the mutex, this call will block
* forever.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_lock(mtx_t *mtx);
/** Lock the given mutex, or block until a specific point in time.
* Blocks until either the given mutex can be locked, or the specified TIME_UTC
* based time.
* @param mtx A mutex object.
* @param ts A UTC based calendar time
* @return @ref The mtx_timedlock function returns thrd_success on success, or
* thrd_timedout if the time specified was reached without acquiring the
* requested resource, or thrd_error if the request could not be honored.
*/
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
/** Try to lock the given mutex.
* The specified mutex shall support either test and return or timeout. If the
* mutex is already locked, the function returns without blocking.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_busy if the resource
* requested is already in use, or @ref thrd_error if the request could not be
* honored.
*/
int mtx_trylock(mtx_t *mtx);
/** Unlock the given mutex.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_unlock(mtx_t *mtx);
/* Condition variable */
typedef pthread_cond_t cnd_t;
/** Create a condition variable object.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_init(cnd_t *cond);
/** Release any resources used by the given condition variable.
* @param cond A condition variable object.
*/
void cnd_destroy(cnd_t *cond);
/** Signal a condition variable.
* Unblocks one of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_signal(cnd_t *cond);
/** Broadcast a condition variable.
* Unblocks all of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_broadcast(cnd_t *cond);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
* before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_wait(cnd_t *cond, mtx_t *mtx);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast, or until after the specified time. When the calling thread
* becomes unblocked it locks the mutex before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @param ts A point in time at which the request will time out (absolute time).
* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
* specified in the call was reached without acquiring the requested resource, or
* @ref thrd_error if the request could not be honored.
*/
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
/* Thread */
typedef pthread_t thrd_t;
/** Thread start function.
* Any thread that is started with the @ref thrd_create() function must be
* started through a function of this type.
* @param arg The thread argument (the @c arg argument of the corresponding
* @ref thrd_create() call).
* @return The thread return value, which can be obtained by another thread
* by using the @ref thrd_join() function.
*/
typedef int (*thrd_start_t)(void *arg);
/** Create a new thread.
* @param thr Identifier of the newly created thread.
* @param func A function pointer to the function that will be executed in
* the new thread.
* @param arg An argument to the thread function.
* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
* be allocated for the thread requested, or @ref thrd_error if the request
* could not be honored.
* @note A thread’s identifier may be reused for a different thread once the
* original thread has exited and either been detached or joined to another
* thread.
*/
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
/** Identify the calling thread.
* @return The identifier of the calling thread.
*/
thrd_t thrd_current(void);
/** Dispose of any resources allocated to the thread when that thread exits.
* @return thrd_success, or thrd_error on error
*/
int thrd_detach(thrd_t thr);
/** Compare two thread identifiers.
* The function determines if two thread identifiers refer to the same thread.
* @return Zero if the two thread identifiers refer to different threads.
* Otherwise a nonzero value is returned.
*/
int thrd_equal(thrd_t thr0, thrd_t thr1);
/** Terminate execution of the calling thread.
* @param res Result code of the calling thread.
*/
CTHREAD_NORETURN void thrd_exit(int res);
/** Wait for a thread to terminate.
* The function joins the given thread with the current thread by blocking
* until the other thread has terminated.
* @param thr The thread to join with.
* @param res If this pointer is not NULL, the function will store the result
* code of the given thread in the integer pointed to by @c res.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int thrd_join(thrd_t thr, int *res);
/** Put the calling thread to sleep.
* Suspend execution of the calling thread.
* @param duration Interval to sleep for
* @param remaining If non-NULL, this parameter will hold the remaining
* time until time_point upon return. This will
* typically be zero, but if the thread was woken up
* by a signal that is not ignored before duration was
* reached @c remaining will hold a positive time.
* @return 0 (zero) on successful sleep, -1 if an interrupt occurred,
* or a negative value if the operation fails.
*/
int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
/** Yield execution to another thread.
* Permit other threads to run, even if the current thread would ordinarily
* continue to run.
*/
void thrd_yield(void);
/* Thread local storage */
typedef pthread_key_t tss_t;
/** Destructor function for a thread-specific storage.
* @param val The value of the destructed thread-specific storage.
*/
typedef void (*tss_dtor_t)(void *val);
/** Create a thread-specific storage.
* @param key The unique key identifier that will be set if the function is
* successful.
* @param dtor Destructor function. This can be NULL.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
* @note On Windows, the @c dtor will definitely be called when
* appropriate for threads created with @ref thrd_create. It will be
* called for other threads in most cases, the possible exception being
* for DLLs loaded with LoadLibraryEx. In order to be certain, you
* should use @ref thrd_create whenever possible.
*/
int tss_create(tss_t *key, tss_dtor_t dtor);
/** Delete a thread-specific storage.
* The function releases any resources used by the given thread-specific
* storage.
* @param key The key that shall be deleted.
*/
void tss_delete(tss_t key);
/** Get the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @return The value for the current thread held in the given thread-specific
* storage.
*/
void *tss_get(tss_t key);
/** Set the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @param val The value of the thread-specific storage to set for the current
* thread.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int tss_set(tss_t key, void *val);
#define once_flag pthread_once_t
#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
/** Invoke a callback exactly once
* @param flag Flag used to ensure the callback is invoked exactly
* once.
* @param func Callback to invoke.
*/
#define call_once(flag,func) pthread_once(flag,func)
#ifdef __cplusplus
}
#endif
#endif /* HAS_C11_THREADS */
#endif /* _CTHREAD_H_ */
#if defined(__APPLE__) || defined(__MACH__)
#include <mach/clock.h>
#include <mach/mach.h>
int timespec_get(struct timespec *ts, int base);
#endif
#ifndef _CTHREAD_EXTRA_H_
#define _CTHREAD_EXTRA_H_
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(ZE_MALLOC) || !defined(ZE_FREE) || !defined(ZE_REALLOC)|| !defined(ZE_CALLOC)
#include "rpmalloc.h"
#define ZE_MALLOC malloc
#define ZE_FREE free
#define ZE_REALLOC realloc
#define ZE_CALLOC calloc
#define ZE_MEMALIGN memalign
#endif
#ifndef TIME_UTC
#define TIME_UTC 1
#endif
/* Public API qualifier. */
#ifndef C_API
#define C_API extern
#endif
#ifndef thrd_local
#if defined(emulate_tls) || defined(__TINYC__)
# define thrd_local_return(type, var) return (type *)tss_get(thrd_##var##_tss);
# define thrd_local_get(type, var, initial, prefix) \
prefix type* var(void) { \
if (thrd_##var##_tls == 0) { \
thrd_##var##_tls = sizeof(type); \
if (tss_create(&thrd_##var##_tss, ZE_FREE) == thrd_success) \
atexit(var##_del); \
else \
goto err; \
} \
void *ptr = tss_get(thrd_##var##_tss); \
if (ptr == NULL) { \
ptr = ZE_MALLOC(thrd_##var##_tls); \
if (ptr == NULL) \
goto err; \
if ((tss_set(thrd_##var##_tss, ptr)) != thrd_success) \
goto err; \
} \
return (type *)ptr; \
err: \
return NULL; \
}
# define thrd_local_del(type, var, initial, prefix) \
prefix void var##_del(void) { \
if (thrd_##var##_tls != 0) { \
thrd_##var##_tls = 0; \
ZE_FREE(tss_get(thrd_##var##_tss)); \
tss_delete(thrd_##var##_tss); \
thrd_##var##_tss = -1; \
} \
}
# define thrd_local_setup(type, var, initial, prefix) \
static type thrd_##var##_buffer; \
prefix int thrd_##var##_tls = 0; \
prefix tss_t thrd_##var##_tss = 0; \
thrd_local_del(type, var, initial, prefix) \
prefix FORCEINLINE void var##_update(type *value) { \
*var() = *value; \
} \
prefix FORCEINLINE bool is_##var##_empty(void) { \
return (type *)tss_get(thrd_##var##_tss) == (type *)initial; \
}
/* Initialize and setup thread local storage `var name` as functions. */
# define thrd_local(type, var, initial) \
thrd_local_setup(type, var, initial, ) \
thrd_local_get(type, var, initial, )
# define thrd_local_plain(type, var, initial) \
thrd_local_setup(type, var, initial, ) \
thrd_local_get(type, var, initial, )
# define thrd_static(type, var, initial) \
static type *var(void); \
static void var##_del(void); \
static bool is_##var##_empty(void); \
thrd_local_setup(type, var, initial, static) \
thrd_local_get(type, var, initial, static)
# define thrd_static_plain(type, var, initial) thrd_static(type, var, initial)
# define thrd_local_proto(type, var, prefix) \
prefix int thrd_##var##_tls; \
prefix tss_t thrd_##var##_tss; \
prefix type var(void); \
prefix void var##_del(void); \
prefix void var##_update(type value); \
prefix bool is_##var##_empty(void);
/* Creates a `extern` compile time emulated/explict thread-local storage variable, pointer of type */
# define thrd_local_extern(type, var) thrd_local_proto(type *, var, C_API)
/* Creates a `extern` compile time emulated/explict thread-local storage variable, non-pointer of type */
# define thrd_local_external(type, var) thrd_local_proto(type, var, C_API)
#else
# define thrd_local_return(type, var) return (type)thrd_##var##_tls;
# define thrd_local_get(type, var, initial, prefix) \
prefix FORCEINLINE type var(void) { \
if (thrd_##var##_tls == initial) { \
thrd_##var##_tls = &thrd_##var##_buffer; \
atexit(var##_del); \
} \
thrd_local_return(type, var) \
}
# define thrd_local_setup(type, var, initial, prefix) \
prefix thread_local type thrd_##var##_tls = initial; \
prefix FORCEINLINE void var##_del(void) { \
thrd_##var##_tls = NULL; \
} \
prefix FORCEINLINE void var##_update(type value) { \
thrd_##var##_tls = value; \
} \
prefix FORCEINLINE bool is_##var##_empty(void) { \
return thrd_##var##_tls == initial; \
}
/* Initialize and setup thread local storage `var name` as functions. */
# define thrd_local(type, var, initial) \
static thread_local type thrd_##var##_buffer; \
thrd_local_setup(type *, var, initial, ) \
thrd_local_get(type *, var, initial, )
# define thrd_local_plain(type, var, initial) \
static thread_local type thrd_##var##_buffer; \
thrd_local_setup(type, var, initial, ) \
thrd_local_get(type, var, initial, )
# define thrd_static(type, var, initial) \
static thread_local type thrd_##var##_buffer; \
thrd_local_setup(type *, var, initial, static) \
thrd_local_get(type *, var, initial, static)
# define thrd_static_plain(type, var, initial) \
static thread_local type thrd_##var##_buffer; \
thrd_local_setup(type, var, initial, static) \
thrd_local_get(type, var, initial, static)
# define thrd_local_proto(type, var, prefix) \
prefix thread_local type thrd_##var##_tls; \
prefix void var##_del(void); \
prefix void var##_update(type value); \
prefix bool is_##var##_empty(void); \
prefix type var(void);
/* Creates a native `extern` compile time thread-local storage variable, pointer of type */
# define thrd_local_extern(type, var) thrd_local_proto(type *, var, C_API)
/* Creates a native `extern` compile time thread-local storage variable, non-pointer of type */
# define thrd_local_external(type, var) thrd_local_proto(type, var, C_API)
#endif
#endif /* thrd_local */
#ifdef __cplusplus
}
#endif
#endif /* _CTHREAD_EXTRA_H_ */