/simpletypesystem/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/simpletypesystem/trunk
128 by Gustav Hartvigsson
* Removed blasted #pragma message s from Thread.h
1
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
2
Copyright (c) 2012 Marcus Geelnard
3
Copyright (c) 2013-2014 Evan Nemerson
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
    1. The origin of this software must not be misrepresented; you must not
14
    claim that you wrote the original software. If you use this software
15
    in a product, an acknowledgment in the product documentation would be
16
    appreciated but is not required.
17
18
    2. Altered source versions must be plainly marked as such, and must not be
19
    misrepresented as being the original software.
20
21
    3. This notice may not be removed or altered from any source
22
    distribution.
23
*/
24
25
#include "tinycthread.h"
26
#include <stdlib.h>
27
28
/* Platform specific includes */
29
#if defined(_TTHREAD_POSIX_)
30
  #include <signal.h>
31
  #include <sched.h>
32
  #include <unistd.h>
33
  #include <sys/time.h>
34
  #include <errno.h>
35
#elif defined(_TTHREAD_WIN32_)
36
  #include <process.h>
37
  #include <sys/timeb.h>
38
#endif
39
40
/* Standard, good-to-have defines */
41
#ifndef NULL
42
  #define NULL (void*)0
43
#endif
44
#ifndef TRUE
45
  #define TRUE 1
46
#endif
47
#ifndef FALSE
48
  #define FALSE 0
49
#endif
50
51
#ifdef __cplusplus
52
extern "C" {
53
#endif
54
55
56
int mtx_init(mtx_t *mtx, int type)
57
{
58
#if defined(_TTHREAD_WIN32_)
59
  mtx->mAlreadyLocked = FALSE;
60
  mtx->mRecursive = type & mtx_recursive;
61
  mtx->mTimed = type & mtx_timed;
62
  if (!mtx->mTimed)
63
  {
64
    InitializeCriticalSection(&(mtx->mHandle.cs));
65
  }
66
  else
67
  {
68
    mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL);
69
    if (mtx->mHandle.mut == NULL)
70
    {
71
      return thrd_error;
72
    }
73
  }
74
  return thrd_success;
75
#else
76
  int ret;
77
  pthread_mutexattr_t attr;
78
  pthread_mutexattr_init(&attr);
79
  if (type & mtx_recursive)
80
  {
81
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
82
  }
83
  ret = pthread_mutex_init(mtx, &attr);
84
  pthread_mutexattr_destroy(&attr);
85
  return ret == 0 ? thrd_success : thrd_error;
86
#endif
87
}
88
89
void mtx_destroy(mtx_t *mtx)
90
{
91
#if defined(_TTHREAD_WIN32_)
92
  if (!mtx->mTimed)
93
  {
94
    DeleteCriticalSection(&(mtx->mHandle.cs));
95
  }
96
  else
97
  {
98
    CloseHandle(mtx->mHandle.mut);
99
  }
100
#else
101
  pthread_mutex_destroy(mtx);
102
#endif
103
}
104
105
int mtx_lock(mtx_t *mtx)
106
{
107
#if defined(_TTHREAD_WIN32_)
108
  if (!mtx->mTimed)
109
  {
110
    EnterCriticalSection(&(mtx->mHandle.cs));
111
  }
112
  else
113
  {
114
    switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE))
115
    {
116
      case WAIT_OBJECT_0:
117
        break;
118
      case WAIT_ABANDONED:
119
      default:
120
        return thrd_error;
121
    }
122
  }
123
124
  if (!mtx->mRecursive)
125
  {
126
    while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
127
    mtx->mAlreadyLocked = TRUE;
128
  }
129
  return thrd_success;
130
#else
131
  return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
132
#endif
133
}
134
135
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
136
{
137
#if defined(_TTHREAD_WIN32_)
138
  struct timespec current_ts;
139
  DWORD timeoutMs;
140
141
  if (!mtx->mTimed)
142
  {
143
    return thrd_error;
144
  }
145
146
  timespec_get(&current_ts, TIME_UTC);
147
148
  if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec)))
149
  {
150
    timeoutMs = 0;
151
  }
152
  else
153
  {
154
    timeoutMs  = (DWORD)(ts->tv_sec  - current_ts.tv_sec)  * 1000;
155
    timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000;
156
    timeoutMs += 1;
157
  }
158
159
  /* TODO: the timeout for WaitForSingleObject doesn't include time
160
     while the computer is asleep. */
161
  switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs))
162
  {
163
    case WAIT_OBJECT_0:
164
      break;
165
    case WAIT_TIMEOUT:
166
      return thrd_timedout;
167
    case WAIT_ABANDONED:
168
    default:
169
      return thrd_error;
170
  }
171
172
  if (!mtx->mRecursive)
173
  {
174
    while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
175
    mtx->mAlreadyLocked = TRUE;
176
  }
177
178
  return thrd_success;
179
#elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L)
180
  switch (pthread_mutex_timedlock(mtx, ts)) {
181
    case 0:
182
      return thrd_success;
183
    case ETIMEDOUT:
184
      return thrd_timedout;
185
    default:
186
      return thrd_error;
187
  }
188
#else
189
  int rc;
190
  struct timespec cur, dur;
191
192
  /* Try to acquire the lock and, if we fail, sleep for 5ms. */
193
  while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) {
194
    timespec_get(&cur, TIME_UTC);
195
196
    if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec)))
197
    {
198
      break;
199
    }
200
201
    dur.tv_sec = ts->tv_sec - cur.tv_sec;
202
    dur.tv_nsec = ts->tv_nsec - cur.tv_nsec;
203
    if (dur.tv_nsec < 0)
204
    {
205
      dur.tv_sec--;
206
      dur.tv_nsec += 1000000000;
207
    }
208
209
    if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000))
210
    {
211
      dur.tv_sec = 0;
212
      dur.tv_nsec = 5000000;
213
    }
214
215
    nanosleep(&dur, NULL);
216
  }
217
218
  switch (rc) {
219
    case 0:
220
      return thrd_success;
221
    case ETIMEDOUT:
222
    case EBUSY:
223
      return thrd_timedout;
224
    default:
225
      return thrd_error;
226
  }
227
#endif
228
}
229
230
int mtx_trylock(mtx_t *mtx)
231
{
232
#if defined(_TTHREAD_WIN32_)
233
  int ret;
234
235
  if (!mtx->mTimed)
236
  {
237
    ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy;
238
  }
239
  else
240
  {
241
    ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy;
242
  }
243
244
  if ((!mtx->mRecursive) && (ret == thrd_success))
245
  {
246
    if (mtx->mAlreadyLocked)
247
    {
248
      LeaveCriticalSection(&(mtx->mHandle.cs));
249
      ret = thrd_busy;
250
    }
251
    else
252
    {
253
      mtx->mAlreadyLocked = TRUE;
254
    }
255
  }
256
  return ret;
257
#else
258
  return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
259
#endif
260
}
261
262
int mtx_unlock(mtx_t *mtx)
263
{
264
#if defined(_TTHREAD_WIN32_)
265
  mtx->mAlreadyLocked = FALSE;
266
  if (!mtx->mTimed)
267
  {
268
    LeaveCriticalSection(&(mtx->mHandle.cs));
269
  }
270
  else
271
  {
272
    if (!ReleaseMutex(mtx->mHandle.mut))
273
    {
274
      return thrd_error;
275
    }
276
  }
277
  return thrd_success;
278
#else
279
  return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
280
#endif
281
}
282
283
#if defined(_TTHREAD_WIN32_)
284
#define _CONDITION_EVENT_ONE 0
285
#define _CONDITION_EVENT_ALL 1
286
#endif
287
288
int cnd_init(cnd_t *cond)
289
{
290
#if defined(_TTHREAD_WIN32_)
291
  cond->mWaitersCount = 0;
292
293
  /* Init critical section */
294
  InitializeCriticalSection(&cond->mWaitersCountLock);
295
296
  /* Init events */
297
  cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
298
  if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
299
  {
300
    cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
301
    return thrd_error;
302
  }
303
  cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
304
  if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
305
  {
306
    CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
307
    cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
308
    return thrd_error;
309
  }
310
311
  return thrd_success;
312
#else
313
  return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
314
#endif
315
}
316
317
void cnd_destroy(cnd_t *cond)
318
{
319
#if defined(_TTHREAD_WIN32_)
320
  if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
321
  {
322
    CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
323
  }
324
  if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
325
  {
326
    CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
327
  }
328
  DeleteCriticalSection(&cond->mWaitersCountLock);
329
#else
330
  pthread_cond_destroy(cond);
331
#endif
332
}
333
334
int cnd_signal(cnd_t *cond)
335
{
336
#if defined(_TTHREAD_WIN32_)
337
  int haveWaiters;
338
339
  /* Are there any waiters? */
340
  EnterCriticalSection(&cond->mWaitersCountLock);
341
  haveWaiters = (cond->mWaitersCount > 0);
342
  LeaveCriticalSection(&cond->mWaitersCountLock);
343
344
  /* If we have any waiting threads, send them a signal */
345
  if(haveWaiters)
346
  {
347
    if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
348
    {
349
      return thrd_error;
350
    }
351
  }
352
353
  return thrd_success;
354
#else
355
  return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
356
#endif
357
}
358
359
int cnd_broadcast(cnd_t *cond)
360
{
361
#if defined(_TTHREAD_WIN32_)
362
  int haveWaiters;
363
364
  /* Are there any waiters? */
365
  EnterCriticalSection(&cond->mWaitersCountLock);
366
  haveWaiters = (cond->mWaitersCount > 0);
367
  LeaveCriticalSection(&cond->mWaitersCountLock);
368
369
  /* If we have any waiting threads, send them a signal */
370
  if(haveWaiters)
371
  {
372
    if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
373
    {
374
      return thrd_error;
375
    }
376
  }
377
378
  return thrd_success;
379
#else
380
  return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error;
381
#endif
382
}
383
384
#if defined(_TTHREAD_WIN32_)
385
static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
386
{
387
  int result, lastWaiter;
388
389
  /* Increment number of waiters */
390
  EnterCriticalSection(&cond->mWaitersCountLock);
391
  ++ cond->mWaitersCount;
392
  LeaveCriticalSection(&cond->mWaitersCountLock);
393
394
  /* Release the mutex while waiting for the condition (will decrease
395
     the number of waiters when done)... */
396
  mtx_unlock(mtx);
397
398
  /* Wait for either event to become signaled due to cnd_signal() or
399
     cnd_broadcast() being called */
400
  result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
401
  if (result == WAIT_TIMEOUT)
402
  {
403
    /* The mutex is locked again before the function returns, even if an error occurred */
404
    mtx_lock(mtx);
405
    return thrd_timedout;
406
  }
407
  else if (result == (int)WAIT_FAILED)
408
  {
409
    /* The mutex is locked again before the function returns, even if an error occurred */
410
    mtx_lock(mtx);
411
    return thrd_error;
412
  }
413
414
  /* Check if we are the last waiter */
415
  EnterCriticalSection(&cond->mWaitersCountLock);
416
  -- cond->mWaitersCount;
417
  lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
418
               (cond->mWaitersCount == 0);
419
  LeaveCriticalSection(&cond->mWaitersCountLock);
420
421
  /* If we are the last waiter to be notified to stop waiting, reset the event */
422
  if (lastWaiter)
423
  {
424
    if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
425
    {
426
      /* The mutex is locked again before the function returns, even if an error occurred */
427
      mtx_lock(mtx);
428
      return thrd_error;
429
    }
430
  }
431
432
  /* Re-acquire the mutex */
433
  mtx_lock(mtx);
434
435
  return thrd_success;
436
}
437
#endif
438
439
int cnd_wait(cnd_t *cond, mtx_t *mtx)
440
{
441
#if defined(_TTHREAD_WIN32_)
442
  return _cnd_timedwait_win32(cond, mtx, INFINITE);
443
#else
444
  return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
445
#endif
446
}
447
448
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
449
{
450
#if defined(_TTHREAD_WIN32_)
451
  struct timespec now;
452
  if (timespec_get(&now, TIME_UTC) == TIME_UTC)
453
  {
454
    unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000;
455
    unsigned long long tsInMilliseconds  = ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
456
    DWORD delta = (tsInMilliseconds > nowInMilliseconds) ?
457
      (DWORD)(tsInMilliseconds - nowInMilliseconds) : 0;
458
    return _cnd_timedwait_win32(cond, mtx, delta);
459
  }
460
  else
461
    return thrd_error;
462
#else
463
  int ret;
464
  ret = pthread_cond_timedwait(cond, mtx, ts);
465
  if (ret == ETIMEDOUT)
466
  {
467
    return thrd_timedout;
468
  }
469
  return ret == 0 ? thrd_success : thrd_error;
470
#endif
471
}
472
473
#if defined(_TTHREAD_WIN32_)
474
struct TinyCThreadTSSData {
475
  void* value;
476
  tss_t key;
477
  struct TinyCThreadTSSData* next;
478
};
479
480
static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, };
481
482
static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL;
483
static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL;
484
485
static void _tinycthread_tss_cleanup (void);
486
487
static void _tinycthread_tss_cleanup (void) {
488
  struct TinyCThreadTSSData* data;
489
  int iteration;
490
  unsigned int again = 1;
491
  void* value;
492
493
  for (iteration = 0 ; iteration < TSS_DTOR_ITERATIONS && again > 0 ; iteration++)
494
  {
495
    again = 0;
496
    for (data = _tinycthread_tss_head ; data != NULL ; data = data->next)
497
    {
498
      if (data->value != NULL)
499
      {
500
        value = data->value;
501
        data->value = NULL;
502
503
        if (_tinycthread_tss_dtors[data->key] != NULL)
504
        {
505
          again = 1;
506
          _tinycthread_tss_dtors[data->key](value);
507
        }
508
      }
509
    }
510
  }
511
512
  while (_tinycthread_tss_head != NULL) {
513
    data = _tinycthread_tss_head->next;
514
    free (_tinycthread_tss_head);
515
    _tinycthread_tss_head = data;
516
  }
517
  _tinycthread_tss_head = NULL;
518
  _tinycthread_tss_tail = NULL;
519
}
520
521
static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv)
522
{
523
  (void)h;
524
  (void)pv;
525
526
  if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH))
527
  {
528
    _tinycthread_tss_cleanup();
529
  }
530
}
531
532
#if defined(_MSC_VER)
533
  #ifdef _M_X64
534
    #pragma const_seg(".CRT$XLB")
535
  #else
536
    #pragma data_seg(".CRT$XLB")
537
  #endif
538
  PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback;
539
  #ifdef _M_X64
540
    #pragma data_seg()
541
  #else
542
    #pragma const_seg()
543
  #endif
544
#else
545
  PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback;
546
#endif
547
548
#endif /* defined(_TTHREAD_WIN32_) */
549
550
/** Information to pass to the new thread (what to run). */
551
typedef struct {
552
  thrd_start_t mFunction; /**< Pointer to the function to be executed. */
553
  void * mArg;            /**< Function argument for the thread function. */
554
} _thread_start_info;
555
556
/* Thread wrapper function. */
557
#if defined(_TTHREAD_WIN32_)
558
static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg)
559
#elif defined(_TTHREAD_POSIX_)
560
static void * _thrd_wrapper_function(void * aArg)
561
#endif
562
{
563
  thrd_start_t fun;
564
  void *arg;
565
  int  res;
566
567
  /* Get thread startup information */
568
  _thread_start_info *ti = (_thread_start_info *) aArg;
569
  fun = ti->mFunction;
570
  arg = ti->mArg;
571
572
  /* The thread is responsible for freeing the startup information */
573
  free((void *)ti);
574
575
  /* Call the actual client thread function */
576
  res = fun(arg);
577
578
#if defined(_TTHREAD_WIN32_)
579
  if (_tinycthread_tss_head != NULL)
580
  {
581
    _tinycthread_tss_cleanup();
582
  }
583
584
  return (DWORD)res;
585
#else
586
  return (void*)(intptr_t)res;
587
#endif
588
}
589
590
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
591
{
592
  /* Fill out the thread startup information (passed to the thread wrapper,
593
     which will eventually free it) */
594
  _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
595
  if (ti == NULL)
596
  {
597
    return thrd_nomem;
598
  }
599
  ti->mFunction = func;
600
  ti->mArg = arg;
601
602
  /* Create the thread */
603
#if defined(_TTHREAD_WIN32_)
604
  *thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL);
605
#elif defined(_TTHREAD_POSIX_)
606
  if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
607
  {
608
    *thr = 0;
609
  }
610
#endif
611
612
  /* Did we fail to create the thread? */
613
  if(!*thr)
614
  {
615
    free(ti);
616
    return thrd_error;
617
  }
618
619
  return thrd_success;
620
}
621
622
thrd_t thrd_current(void)
623
{
624
#if defined(_TTHREAD_WIN32_)
625
  return GetCurrentThread();
626
#else
627
  return pthread_self();
628
#endif
629
}
630
631
int thrd_detach(thrd_t thr)
632
{
633
#if defined(_TTHREAD_WIN32_)
634
  /* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */
635
  return CloseHandle(thr) != 0 ? thrd_success : thrd_error;
636
#else
637
  return pthread_detach(thr) == 0 ? thrd_success : thrd_error;
638
#endif
639
}
640
641
int thrd_equal(thrd_t thr0, thrd_t thr1)
642
{
643
#if defined(_TTHREAD_WIN32_)
644
  return thr0 == thr1;
645
#else
646
  return pthread_equal(thr0, thr1);
647
#endif
648
}
649
650
void thrd_exit(int res)
651
{
652
#if defined(_TTHREAD_WIN32_)
653
  if (_tinycthread_tss_head != NULL)
654
  {
655
    _tinycthread_tss_cleanup();
656
  }
657
658
  ExitThread(res);
659
#else
660
  pthread_exit((void*)(intptr_t)res);
661
#endif
662
}
663
664
int thrd_join(thrd_t thr, int *res)
665
{
666
#if defined(_TTHREAD_WIN32_)
667
  DWORD dwRes;
668
669
  if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
670
  {
671
    return thrd_error;
672
  }
673
  if (res != NULL)
674
  {
675
    if (GetExitCodeThread(thr, &dwRes) != 0)
676
    {
677
      *res = dwRes;
678
    }
679
    else
680
    {
681
      return thrd_error;
682
    }
683
  }
684
  CloseHandle(thr);
685
#elif defined(_TTHREAD_POSIX_)
686
  void *pres;
687
  if (pthread_join(thr, &pres) != 0)
688
  {
689
    return thrd_error;
690
  }
691
  if (res != NULL)
692
  {
693
    *res = (int)(intptr_t)pres;
694
  }
695
#endif
696
  return thrd_success;
697
}
698
699
int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
700
{
701
#if !defined(_TTHREAD_WIN32_)
702
  return nanosleep(duration, remaining);
703
#else
704
  struct timespec start;
705
  DWORD t;
706
707
  timespec_get(&start, TIME_UTC);
708
709
  t = SleepEx((DWORD)(duration->tv_sec * 1000 +
710
              duration->tv_nsec / 1000000 +
711
              (((duration->tv_nsec % 1000000) == 0) ? 0 : 1)),
712
              TRUE);
713
714
  if (t == 0) {
715
    return 0;
716
  } else if (remaining != NULL) {
717
    timespec_get(remaining, TIME_UTC);
718
    remaining->tv_sec -= start.tv_sec;
719
    remaining->tv_nsec -= start.tv_nsec;
720
    if (remaining->tv_nsec < 0)
721
    {
722
      remaining->tv_nsec += 1000000000;
723
      remaining->tv_sec -= 1;
724
    }
725
  } else {
726
    return -1;
727
  }
728
729
  return 0;
730
#endif
731
}
732
733
void thrd_yield(void)
734
{
735
#if defined(_TTHREAD_WIN32_)
736
  Sleep(0);
737
#else
738
  sched_yield();
739
#endif
740
}
741
742
int tss_create(tss_t *key, tss_dtor_t dtor)
743
{
744
#if defined(_TTHREAD_WIN32_)
745
  *key = TlsAlloc();
746
  if (*key == TLS_OUT_OF_INDEXES)
747
  {
748
    return thrd_error;
749
  }
750
  _tinycthread_tss_dtors[*key] = dtor;
751
#else
752
  if (pthread_key_create(key, dtor) != 0)
753
  {
754
    return thrd_error;
755
  }
756
#endif
757
  return thrd_success;
758
}
759
760
void tss_delete(tss_t key)
761
{
762
#if defined(_TTHREAD_WIN32_)
763
  struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key);
764
  struct TinyCThreadTSSData* prev = NULL;
765
  if (data != NULL)
766
  {
767
    if (data == _tinycthread_tss_head)
768
    {
769
      _tinycthread_tss_head = data->next;
770
    }
771
    else
772
    {
773
      prev = _tinycthread_tss_head;
774
      if (prev != NULL)
775
      {
776
        while (prev->next != data)
777
        {
778
          prev = prev->next;
779
        }
780
      }
781
    }
782
783
    if (data == _tinycthread_tss_tail)
784
    {
785
      _tinycthread_tss_tail = prev;
786
    }
787
788
    free (data);
789
  }
790
  _tinycthread_tss_dtors[key] = NULL;
791
  TlsFree(key);
792
#else
793
  pthread_key_delete(key);
794
#endif
795
}
796
797
void *tss_get(tss_t key)
798
{
799
#if defined(_TTHREAD_WIN32_)
800
  struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
801
  if (data == NULL)
802
  {
803
    return NULL;
804
  }
805
  return data->value;
806
#else
807
  return pthread_getspecific(key);
808
#endif
809
}
810
811
int tss_set(tss_t key, void *val)
812
{
813
#if defined(_TTHREAD_WIN32_)
814
  struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
815
  if (data == NULL)
816
  {
817
    data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData));
818
    if (data == NULL)
819
    {
820
      return thrd_error;
821
	}
822
823
    data->value = NULL;
824
    data->key = key;
825
    data->next = NULL;
826
827
    if (_tinycthread_tss_tail != NULL)
828
    {
829
      _tinycthread_tss_tail->next = data;
830
    }
831
    else
832
    {
833
      _tinycthread_tss_tail = data;
834
    }
835
836
    if (_tinycthread_tss_head == NULL)
837
    {
838
      _tinycthread_tss_head = data;
839
    }
840
841
    if (!TlsSetValue(key, data))
842
    {
843
      free (data);
844
	  return thrd_error;
845
    }
846
  }
847
  data->value = val;
848
#else
849
  if (pthread_setspecific(key, val) != 0)
850
  {
851
    return thrd_error;
852
  }
853
#endif
854
  return thrd_success;
855
}
856
857
#if defined(_TTHREAD_EMULATE_TIMESPEC_GET_)
858
int _tthread_timespec_get(struct timespec *ts, int base)
859
{
860
#if defined(_TTHREAD_WIN32_)
861
  struct _timeb tb;
862
#elif !defined(CLOCK_REALTIME)
863
  struct timeval tv;
864
#endif
865
866
  if (base != TIME_UTC)
867
  {
868
    return 0;
869
  }
870
871
#if defined(_TTHREAD_WIN32_)
872
  _ftime_s(&tb);
873
  ts->tv_sec = (time_t)tb.time;
874
  ts->tv_nsec = 1000000L * (long)tb.millitm;
875
#elif defined(CLOCK_REALTIME)
876
  base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0;
877
#else
878
  gettimeofday(&tv, NULL);
879
  ts->tv_sec = (time_t)tv.tv_sec;
880
  ts->tv_nsec = 1000L * (long)tv.tv_usec;
881
#endif
882
883
  return base;
884
}
885
#endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */
886
887
#if defined(_TTHREAD_WIN32_)
888
void call_once(once_flag *flag, void (*func)(void))
889
{
890
  /* The idea here is that we use a spin lock (via the
891
     InterlockedCompareExchange function) to restrict access to the
892
     critical section until we have initialized it, then we use the
893
     critical section to block until the callback has completed
894
     execution. */
895
  while (flag->status < 3)
896
  {
897
    switch (flag->status)
898
    {
899
      case 0:
900
        if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) {
901
          InitializeCriticalSection(&(flag->lock));
902
          EnterCriticalSection(&(flag->lock));
903
          flag->status = 2;
904
          func();
905
          flag->status = 3;
906
          LeaveCriticalSection(&(flag->lock));
907
          return;
908
        }
909
        break;
910
      case 1:
911
        break;
912
      case 2:
913
        EnterCriticalSection(&(flag->lock));
914
        LeaveCriticalSection(&(flag->lock));
915
        break;
916
    }
917
  }
918
}
919
#endif /* defined(_TTHREAD_WIN32_) */
920
921
#ifdef __cplusplus
922
}
923
#endif