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
int mtx_trylock(mtx_t *mtx)
Endeavors to lock the mutex at mtx.
void mtx_destroy(mtx_t *mtx)
Releases any resources used by the mutex at mtx.
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...
_Noreturn void thrd_exit(int res)
Terminates execution of the calling thread and sets its result code to res.
Indicates that the time specified in the call was reached without acquiring the requested resource...
Definition: threads.h:128
Indicates that the requested operation succeeded.
Definition: threads.h:121
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
int thrd_detach(thrd_t thr)
Tells the operating system to dispose of any resources allocated to the thread identified by thr when...
pthread_t thrd_t
A complete object type that holds an identifier for a thread.
Definition: threads.h:85
Indicates that the requested operation failed.
Definition: threads.h:123
A mutex type that supports neither timeout nor test and return.
Definition: threads.h:109
void * tss_get(tss_t key)
Returns the value for the current thread held in the thread-specific storage identified by key...
int mtx_unlock(mtx_t *mtx)
Unlocks the mutex at mtx.
pthread_key_t tss_t
A complete object type that holds an identifier for a thread-specific storage pointer.
Definition: threads.h:95
Indicates that the requested operation failed because it was unable to allocate memory.
Definition: threads.h:138
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 ...
This header file is part of the C11 and POSIX compatibility library; it includes <threads.h>, if it exists, and defines any missing functionality.
int mtx_lock(mtx_t *mtx)
Blocks until it locks the mutex at mtx.
thrd_t thrd_current(void)
Identifies the thread that called it.
pthread_cond_t cnd_t
A complete object type that holds an identifier for a condition variable.
Definition: threads.h:78
pthread_mutex_t mtx_t
A complete object type that holds an identifier for a mutex.
Definition: threads.h:102
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...
int cnd_init(cnd_t *cond)
Creates a condition variable.
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...
pthread_once_t once_flag
A complete object type that holds a flag for use by call_once().
Definition: threads.h:143
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...
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).
void thrd_yield(void)
Endeavors to permit other threads to run, even if the current thread would ordinarily continue to run...
Indicates that the requested operation failed because a resource requested by a test and return funct...
Definition: threads.h:133
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 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_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...
This header file is part of the C11 and POSIX compatibility library; it includes <stdint.h> and defines any missing functionality.
int(* thrd_start_t)(void *)
The function pointer type that is passed to thrd_create() to create a new thread. ...
Definition: threads.h:168
void cnd_destroy(cnd_t *cond)
Releases all resources used by the condition variable at cond.
A mutex type that supports timeout (not available with the native Windows API).
Definition: threads.h:116
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib.h> and defines any missing functionality.
#define _Noreturn
A function declared with a _Noreturn function specifier SHALL not return to its caller.
Definition: features.h:214
int tss_create(tss_t *key, tss_dtor_t dtor)
Creates a thread-specific storage pointer with destructor dtor, which may be NULL.
void tss_delete(tss_t key)
Releases any resources used by the thread-specific storage identified by key.
This is the internal header file of the C11 and POSIX compatibility library.
void(* tss_dtor_t)(void *)
The function pointer type used for a destructor for a thread-specific storage pointer.
Definition: threads.h:157
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 mtx_init(mtx_t *mtx, int type)
Creates a mutex object with properties indicated by type, which must have one of the four values: ...