Lely core libraries  2.2.5
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 
36 struct once_info {
38  struct once_info *next;
40  once_flag *flag;
42  size_t cnt;
44  mtx_t mtx;
45 };
46 
48 static volatile LONG once_lock;
49 
54 static struct once_info *once_list;
55 
60 static struct once_info once_fast;
61 
66 struct 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 
95 static thread_local struct thrd_info *thrd_self;
96 
101 static void thrd_start(void *arglist);
102 
103 void
104 call_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 
175 int
176 cnd_broadcast(cnd_t *cond)
177 {
178  WakeAllConditionVariable(cond);
179 
180  return thrd_success;
181 }
182 
183 void
184 cnd_destroy(cnd_t *cond)
185 {
186  (void)cond;
187 }
188 
189 int
190 cnd_init(cnd_t *cond)
191 {
192  InitializeConditionVariable(cond);
193 
194  return thrd_success;
195 }
196 
197 int
198 cnd_signal(cnd_t *cond)
199 {
200  WakeConditionVariable(cond);
201 
202  return thrd_success;
203 }
204 
205 int
206 cnd_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 
236 int
237 cnd_wait(cnd_t *cond, mtx_t *mtx)
238 {
239  // clang-format off
240  return SleepConditionVariableCS(cond, mtx, INFINITE)
241  ? thrd_success
242  : thrd_error;
243  // clang-format on
244 }
245 
246 void
247 mtx_destroy(mtx_t *mtx)
248 {
249  DeleteCriticalSection(mtx);
250 }
251 
252 int
253 mtx_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 
263 int
264 mtx_lock(mtx_t *mtx)
265 {
266  EnterCriticalSection(mtx);
267 
268  return thrd_success;
269 }
270 
271 int
272 mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
273 {
274  (void)mtx;
275  (void)ts;
276 
277  return thrd_error;
278 }
279 
280 int
281 mtx_trylock(mtx_t *mtx)
282 {
283  return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
284 }
285 
286 int
287 mtx_unlock(mtx_t *mtx)
288 {
289  LeaveCriticalSection(mtx);
290 
291  return thrd_success;
292 }
293 
294 int
295 thrd_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 
321 thrd_t
322 thrd_current(void)
323 {
324  return (thrd_t)thrd_self;
325 }
326 
327 int
328 thrd_detach(thrd_t thr)
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 
348 int
349 thrd_equal(thrd_t thr0, thrd_t thr1)
350 {
351  return thr0 == thr1;
352 }
353 
354 _Noreturn void
355 thrd_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 
380 int
381 thrd_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 
406 int
407 thrd_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 
418 void
419 thrd_yield(void)
420 {
421  SwitchToThread();
422 }
423 
424 int
425 tss_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 
436 void
437 tss_delete(tss_t key)
438 {
439  FlsFree(key);
440 }
441 
442 void *
443 tss_get(tss_t key)
444 {
445  return FlsGetValue(key);
446 }
447 
448 int
449 tss_set(tss_t key, void *val)
450 {
451  return FlsSetValue(key, val) ? thrd_success : thrd_error;
452 }
453 
454 static void
455 thrd_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:214
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:202
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.
void * tss_get(tss_t key)
Returns the value for the current thread held in the thread-specific storage identified by key.
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
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