Lely core libraries 2.3.4
threads-win32.c
Go to the documentation of this file.
1
23#include "libc.h"
24#include <lely/libc/threads.h>
25
26#if !LELY_NO_THREADS && _WIN32 && !LELY_HAVE_PTHREAD_H
27
28#include <assert.h>
29#include <errno.h>
30#include <stdint.h>
31#include <stdlib.h>
32
33#include <process.h>
34
36struct once_info {
38 struct once_info *next;
40 once_flag *flag;
42 size_t cnt;
44 mtx_t mtx;
45};
46
48static volatile LONG once_lock;
49
54static struct once_info *once_list;
55
60static struct once_info once_fast;
61
66struct thrd_info {
68 thrd_start_t func;
70 void *arg;
72 int res;
74 cnd_t cond;
76 mtx_t mtx;
78 enum {
80 THRD_STARTED,
85 THRD_STOPPED,
87 THRD_DETACHED
88 } stat;
89};
90
95static thread_local struct thrd_info *thrd_self;
96
101static void thrd_start(void *arglist);
102
103void
104call_once(once_flag *flag, void (*func)(void))
105{
106 assert(flag);
107 assert(func);
108
109 // Perform a quick (atomic) check to see if the flag is already set.
110 if (InterlockedCompareExchange(flag, 0, 0))
111 return;
112
113 struct once_info *info;
114 struct once_info **pinfo;
115
116 // Acquire the spinlock for once_list.
117 while (InterlockedCompareExchange(&once_lock, 1, 0))
118 thrd_yield();
119
120 // Find the flag in the list and increment its use count. If not found,
121 // create a new entry and initialize the mutex.
122 for (pinfo = &once_list; *pinfo && (*pinfo)->flag != flag;
123 pinfo = &(*pinfo)->next)
124 ;
125 if (*pinfo) {
126 info = *pinfo;
127 info->cnt++;
128 } else {
129 if (once_fast.flag)
130 info = malloc(sizeof(*info));
131 else
132 info = &once_fast;
133 // We cannot signal a malloc() error to the user.
134 assert(info);
135 info->next = NULL;
136 info->flag = flag;
137 info->cnt = 0;
138 mtx_init(&info->mtx, mtx_plain);
139 *pinfo = info;
140 }
141
142 // Release the spinlock for once_list.
143 InterlockedExchange(&once_lock, 0);
144
145 // Now that we have a mutex for the flag, lock it and run func() once.
146 mtx_lock(&info->mtx);
147 if (!InterlockedCompareExchange(info->flag, 0, 0)) {
148 func();
149 InterlockedExchange(info->flag, 1);
150 }
151 mtx_unlock(&info->mtx);
152
153 while (InterlockedCompareExchange(&once_lock, 1, 0))
154 thrd_yield();
155
156 // Find the flag in the list and decrement its use count. If the count
157 // is zero, destroy the mutex and delete the entry.
158 for (pinfo = &once_list; (*pinfo)->flag != flag;
159 pinfo = &(*pinfo)->next)
160 ;
161 assert(*pinfo);
162 info = *pinfo;
163 if (!info->cnt--) {
164 *pinfo = info->next;
165 mtx_destroy(&info->mtx);
166 if (info == &once_fast)
167 info->flag = NULL;
168 else
169 free(info);
170 }
171
172 InterlockedExchange(&once_lock, 0);
173}
174
175int
176cnd_broadcast(cnd_t *cond)
177{
178 WakeAllConditionVariable(cond);
179
180 return thrd_success;
181}
182
183void
184cnd_destroy(cnd_t *cond)
185{
186 (void)cond;
187}
188
189int
190cnd_init(cnd_t *cond)
191{
192 InitializeConditionVariable(cond);
193
194 return thrd_success;
195}
196
197int
198cnd_signal(cnd_t *cond)
199{
200 WakeConditionVariable(cond);
201
202 return thrd_success;
203}
204
205int
206cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
207{
208 struct timespec now = { 0, 0 };
209 timespec_get(&now, TIME_UTC);
210 do {
211 // Round up to the nearest number of milliseconds, to make sure
212 // we don't wake up too early.
213 LONGLONG llMilliseconds =
214 (LONGLONG)(ts->tv_sec - now.tv_sec) * 1000
215 + (ts->tv_nsec - now.tv_nsec + 999999l)
216 / 1000000l;
217 if (llMilliseconds < 0)
218 llMilliseconds = 0;
219 DWORD dwMilliseconds = llMilliseconds <= MAX_SLEEP_MS
220 ? (DWORD)llMilliseconds
221 : MAX_SLEEP_MS;
222 DWORD dwResult = SleepConditionVariableCS(
223 cond, mtx, dwMilliseconds);
224 if (!dwResult)
225 return thrd_success;
226 if (GetLastError() != ERROR_TIMEOUT)
227 return thrd_error;
228 timespec_get(&now, TIME_UTC);
229 // clang-format off
230 } while (now.tv_sec < ts->tv_sec || (now.tv_sec == ts->tv_sec
231 && now.tv_nsec < ts->tv_nsec));
232 // clang-format on
233 return thrd_timedout;
234}
235
236int
237cnd_wait(cnd_t *cond, mtx_t *mtx)
238{
239 // clang-format off
240 return SleepConditionVariableCS(cond, mtx, INFINITE)
242 : thrd_error;
243 // clang-format on
244}
245
246void
247mtx_destroy(mtx_t *mtx)
248{
249 DeleteCriticalSection(mtx);
250}
251
252int
253mtx_init(mtx_t *mtx, int type)
254{
255 if (type & mtx_timed)
256 return thrd_error;
257
258 InitializeCriticalSection(mtx);
259
260 return thrd_success;
261}
262
263int
264mtx_lock(mtx_t *mtx)
265{
266 EnterCriticalSection(mtx);
267
268 return thrd_success;
269}
270
271int
272mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
273{
274 (void)mtx;
275 (void)ts;
276
277 return thrd_error;
278}
279
280int
281mtx_trylock(mtx_t *mtx)
282{
283 return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
284}
285
286int
287mtx_unlock(mtx_t *mtx)
288{
289 LeaveCriticalSection(mtx);
290
291 return thrd_success;
292}
293
294int
295thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
296{
297 struct thrd_info *info = malloc(sizeof(*info));
298 if (!info)
299 return thrd_nomem;
300
301 info->func = func;
302 info->arg = arg;
303 info->res = 0;
304
305 cnd_init(&info->cond);
306 mtx_init(&info->mtx, mtx_plain);
307 info->stat = THRD_STARTED;
308
309 if (_beginthread(&thrd_start, 0, info) == (uintptr_t)-1) {
310 mtx_destroy(&info->mtx);
311 cnd_destroy(&info->cond);
312 free(info);
313 return thrd_error;
314 }
315
316 *thr = (thrd_t)info;
317
318 return thrd_success;
319}
320
321thrd_t
322thrd_current(void)
323{
324 return (thrd_t)thrd_self;
325}
326
327int
329{
330 struct thrd_info *info = (struct thrd_info *)thr;
331
332 mtx_lock(&info->mtx);
333 if (info->stat == THRD_STOPPED) {
334 mtx_unlock(&info->mtx);
335
336 mtx_destroy(&info->mtx);
337 cnd_destroy(&info->cond);
338 free(info);
339 } else {
340 assert(info->stat != THRD_DETACHED);
341 info->stat = THRD_DETACHED;
342 mtx_unlock(&info->mtx);
343 }
344
345 return thrd_success;
346}
347
348int
349thrd_equal(thrd_t thr0, thrd_t thr1)
350{
351 return thr0 == thr1;
352}
353
354_Noreturn void
355thrd_exit(int res)
356{
357 struct thrd_info *info = (struct thrd_info *)thrd_current();
358
359 info->res = res;
360
361 mtx_lock(&info->mtx);
362 if (info->stat == THRD_DETACHED) {
363 mtx_unlock(&info->mtx);
364
365 mtx_destroy(&info->mtx);
366 cnd_destroy(&info->cond);
367 free(info);
368 } else {
369 assert(info->stat != THRD_STOPPED);
370 info->stat = THRD_STOPPED;
371 cnd_signal(&info->cond);
372 mtx_unlock(&info->mtx);
373 }
374
375 _endthread();
376 for (;;)
377 ;
378}
379
380int
381thrd_join(thrd_t thr, int *res)
382{
383 struct thrd_info *info = (struct thrd_info *)thr;
384
385 mtx_lock(&info->mtx);
386 while (info->stat == THRD_STARTED) {
387 if (cnd_wait(&info->cond, &info->mtx) == thrd_error)
388 break;
389 }
390 if (info->stat != THRD_STOPPED) {
391 mtx_unlock(&info->mtx);
392 return thrd_error;
393 }
394 mtx_unlock(&info->mtx);
395
396 if (res)
397 *res = info->res;
398
399 mtx_destroy(&info->mtx);
400 cnd_destroy(&info->cond);
401 free(info);
402
403 return thrd_success;
404}
405
406int
407thrd_sleep(const struct timespec *duration, struct timespec *remaining)
408{
409 int errsv = errno;
410 int res = nanosleep(duration, remaining);
411 if (res) {
412 res = errno == EINTR ? -1 : -2;
413 errno = errsv;
414 }
415 return res;
416}
417
418void
419thrd_yield(void)
420{
421 SwitchToThread();
422}
423
424int
425tss_create(tss_t *key, tss_dtor_t dtor)
426{
427 DWORD dwFlsIndex = FlsAlloc(dtor);
428 if (dwFlsIndex == FLS_OUT_OF_INDEXES)
429 return thrd_error;
430
431 *key = dwFlsIndex;
432
433 return thrd_success;
434}
435
436void
437tss_delete(tss_t key)
438{
439 FlsFree(key);
440}
441
442void *
443tss_get(tss_t key)
444{
445 return FlsGetValue(key);
446}
447
448int
449tss_set(tss_t key, void *val)
450{
451 return FlsSetValue(key, val) ? thrd_success : thrd_error;
452}
453
454static void
455thrd_start(void *arglist)
456{
457 thrd_self = arglist;
458
459 struct thrd_info *info = (struct thrd_info *)thrd_current();
460
461 thrd_exit(info->func(info->arg));
462}
463
464#endif // !LELY_NO_THREADS && _WIN32 && !LELY_HAVE_PTHREAD_H
#define _Noreturn
A function declared with a _Noreturn function specifier SHALL not return to its caller.
Definition: features.h:224
int timespec_get(struct timespec *ts, int base)
Sets the interval at ts to hold the current calendar time based on the specified time base.
Definition: time.c:32
#define TIME_UTC
An integer constant greater than 0 that designates the UTC time base.
Definition: time.h:207
This is the internal header file of the C11 and POSIX compatibility library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdint....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
pthread_key_t tss_t
A complete object type that holds an identifier for a thread-specific storage pointer.
Definition: threads.h:95
int cnd_init(cnd_t *cond)
Creates a condition variable.
int(* thrd_start_t)(void *)
The function pointer type that is passed to thrd_create() to create a new thread.
Definition: threads.h:168
int thrd_equal(thrd_t thr0, thrd_t thr1)
Determines whether the thread identified by thr0 refers to the thread identified by thr1.
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
Creates a new thread executing func(arg).
int tss_create(tss_t *key, tss_dtor_t dtor)
Creates a thread-specific storage pointer with destructor dtor, which may be NULL.
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
Atomically unlocks the mutex at mtx and endeavors to block until the condition variable at cond is si...
_Noreturn void thrd_exit(int res)
Terminates execution of the calling thread and sets its result code to res.
pthread_t thrd_t
A complete object type that holds an identifier for a thread.
Definition: threads.h:85
void * tss_get(tss_t key)
Returns the value for the current thread held in the thread-specific storage identified by key.
int tss_set(tss_t key, void *val)
Sets the value for the current thread held in the thread-specific storage identified by key to val.
int cnd_broadcast(cnd_t *cond)
Unblocks all of the threads that are blocked on the condition variable at cond at the time of the cal...
int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
Suspends execution of the calling thread until either the interval specified by duration has elapsed ...
int mtx_init(mtx_t *mtx, int type)
Creates a mutex object with properties indicated by type, which must have one of the four values:
int mtx_lock(mtx_t *mtx)
Blocks until it locks the mutex at mtx.
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
Endeavors to block until it locks the mutex at mtx or until after the TIME_UTC-based calendar time at...
int thrd_join(thrd_t thr, int *res)
Joins the thread identified by thr with the current thread by blocking until the other thread has ter...
pthread_cond_t cnd_t
A complete object type that holds an identifier for a condition variable.
Definition: threads.h:78
void cnd_destroy(cnd_t *cond)
Releases all resources used by the condition variable at cond.
thrd_t thrd_current(void)
Identifies the thread that called it.
void call_once(once_flag *flag, void(*func)(void))
Uses the once_flag at flag to ensure that func is called exactly once, the first time the call_once()...
int cnd_wait(cnd_t *cond, mtx_t *mtx)
Atomically unlocks the mutex at mtx and endeavors to block until the condition variable at cond is si...
int mtx_trylock(mtx_t *mtx)
Endeavors to lock the mutex at mtx.
void thrd_yield(void)
Endeavors to permit other threads to run, even if the current thread would ordinarily continue to run...
pthread_once_t once_flag
A complete object type that holds a flag for use by call_once().
Definition: threads.h:143
int thrd_detach(thrd_t thr)
Tells the operating system to dispose of any resources allocated to the thread identified by thr when...
@ thrd_timedout
Indicates that the time specified in the call was reached without acquiring the requested resource.
Definition: threads.h:128
@ thrd_success
Indicates that the requested operation succeeded.
Definition: threads.h:121
@ thrd_busy
Indicates that the requested operation failed because a resource requested by a test and return funct...
Definition: threads.h:133
@ thrd_nomem
Indicates that the requested operation failed because it was unable to allocate memory.
Definition: threads.h:138
@ thrd_error
Indicates that the requested operation failed.
Definition: threads.h:123
int mtx_unlock(mtx_t *mtx)
Unlocks the mutex at mtx.
pthread_mutex_t mtx_t
A complete object type that holds an identifier for a mutex.
Definition: threads.h:102
void mtx_destroy(mtx_t *mtx)
Releases any resources used by the mutex at mtx.
void tss_delete(tss_t key)
Releases any resources used by the thread-specific storage identified by key.
int cnd_signal(cnd_t *cond)
Unblocks one of the threads that are blocked on the condition variable at cond at the time of the cal...
@ mtx_timed
A mutex type that supports timeout (not available with the native Windows API).
Definition: threads.h:116
@ mtx_plain
A mutex type that supports neither timeout nor test and return.
Definition: threads.h:109
void(* tss_dtor_t)(void *)
The function pointer type used for a destructor for a thread-specific storage pointer.
Definition: threads.h:157