• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
2 Copyright (c) 2012 Marcus Geelnard
3 
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7 
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11 
12     1. The origin of this software must not be misrepresented; you must not
13     claim that you wrote the original software. If you use this software
14     in a product, an acknowledgment in the product documentation would be
15     appreciated but is not required.
16 
17     2. Altered source versions must be plainly marked as such, and must not be
18     misrepresented as being the original software.
19 
20     3. This notice may not be removed or altered from any source
21     distribution.
22 */
23 
24 /* 2013-01-06 Camilla Berglund <elmindreda@glfw.org>
25  *
26  * Added casts from time_t to DWORD to avoid warnings on VC++.
27  * Fixed time retrieval on POSIX systems.
28  */
29 
30 #include "tinycthread.h"
31 #include <stdlib.h>
32 
33 /* Platform specific includes */
34 #if defined(_TTHREAD_POSIX_)
35   #include <signal.h>
36   #include <sched.h>
37   #include <unistd.h>
38   #include <sys/time.h>
39   #include <errno.h>
40 #elif defined(_TTHREAD_WIN32_)
41   #include <process.h>
42   #include <sys/timeb.h>
43 #endif
44 
45 /* Standard, good-to-have defines */
46 #ifndef NULL
47   #define NULL (void*)0
48 #endif
49 #ifndef TRUE
50   #define TRUE 1
51 #endif
52 #ifndef FALSE
53   #define FALSE 0
54 #endif
55 
mtx_init(mtx_t * mtx,int type)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   InitializeCriticalSection(&mtx->mHandle);
62   return thrd_success;
63 #else
64   int ret;
65   pthread_mutexattr_t attr;
66   pthread_mutexattr_init(&attr);
67   if (type & mtx_recursive)
68   {
69     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
70   }
71   ret = pthread_mutex_init(mtx, &attr);
72   pthread_mutexattr_destroy(&attr);
73   return ret == 0 ? thrd_success : thrd_error;
74 #endif
75 }
76 
mtx_destroy(mtx_t * mtx)77 void mtx_destroy(mtx_t *mtx)
78 {
79 #if defined(_TTHREAD_WIN32_)
80   DeleteCriticalSection(&mtx->mHandle);
81 #else
82   pthread_mutex_destroy(mtx);
83 #endif
84 }
85 
mtx_lock(mtx_t * mtx)86 int mtx_lock(mtx_t *mtx)
87 {
88 #if defined(_TTHREAD_WIN32_)
89   EnterCriticalSection(&mtx->mHandle);
90   if (!mtx->mRecursive)
91   {
92     while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */
93     mtx->mAlreadyLocked = TRUE;
94   }
95   return thrd_success;
96 #else
97   return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
98 #endif
99 }
100 
mtx_timedlock(mtx_t * mtx,const struct timespec * ts)101 int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
102 {
103   /* FIXME! */
104   (void)mtx;
105   (void)ts;
106   return thrd_error;
107 }
108 
mtx_trylock(mtx_t * mtx)109 int mtx_trylock(mtx_t *mtx)
110 {
111 #if defined(_TTHREAD_WIN32_)
112   int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy;
113   if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked)
114   {
115     LeaveCriticalSection(&mtx->mHandle);
116     ret = thrd_busy;
117   }
118   return ret;
119 #else
120   return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
121 #endif
122 }
123 
mtx_unlock(mtx_t * mtx)124 int mtx_unlock(mtx_t *mtx)
125 {
126 #if defined(_TTHREAD_WIN32_)
127   mtx->mAlreadyLocked = FALSE;
128   LeaveCriticalSection(&mtx->mHandle);
129   return thrd_success;
130 #else
131   return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
132 #endif
133 }
134 
135 #if defined(_TTHREAD_WIN32_)
136 #define _CONDITION_EVENT_ONE 0
137 #define _CONDITION_EVENT_ALL 1
138 #endif
139 
cnd_init(cnd_t * cond)140 int cnd_init(cnd_t *cond)
141 {
142 #if defined(_TTHREAD_WIN32_)
143   cond->mWaitersCount = 0;
144 
145   /* Init critical section */
146   InitializeCriticalSection(&cond->mWaitersCountLock);
147 
148   /* Init events */
149   cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
150   if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
151   {
152     cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
153     return thrd_error;
154   }
155   cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
156   if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
157   {
158     CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
159     cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
160     return thrd_error;
161   }
162 
163   return thrd_success;
164 #else
165   return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
166 #endif
167 }
168 
cnd_destroy(cnd_t * cond)169 void cnd_destroy(cnd_t *cond)
170 {
171 #if defined(_TTHREAD_WIN32_)
172   if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
173   {
174     CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
175   }
176   if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
177   {
178     CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
179   }
180   DeleteCriticalSection(&cond->mWaitersCountLock);
181 #else
182   pthread_cond_destroy(cond);
183 #endif
184 }
185 
cnd_signal(cnd_t * cond)186 int cnd_signal(cnd_t *cond)
187 {
188 #if defined(_TTHREAD_WIN32_)
189   int haveWaiters;
190 
191   /* Are there any waiters? */
192   EnterCriticalSection(&cond->mWaitersCountLock);
193   haveWaiters = (cond->mWaitersCount > 0);
194   LeaveCriticalSection(&cond->mWaitersCountLock);
195 
196   /* If we have any waiting threads, send them a signal */
197   if(haveWaiters)
198   {
199     if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
200     {
201       return thrd_error;
202     }
203   }
204 
205   return thrd_success;
206 #else
207   return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
208 #endif
209 }
210 
cnd_broadcast(cnd_t * cond)211 int cnd_broadcast(cnd_t *cond)
212 {
213 #if defined(_TTHREAD_WIN32_)
214   int haveWaiters;
215 
216   /* Are there any waiters? */
217   EnterCriticalSection(&cond->mWaitersCountLock);
218   haveWaiters = (cond->mWaitersCount > 0);
219   LeaveCriticalSection(&cond->mWaitersCountLock);
220 
221   /* If we have any waiting threads, send them a signal */
222   if(haveWaiters)
223   {
224     if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
225     {
226       return thrd_error;
227     }
228   }
229 
230   return thrd_success;
231 #else
232   return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
233 #endif
234 }
235 
236 #if defined(_TTHREAD_WIN32_)
_cnd_timedwait_win32(cnd_t * cond,mtx_t * mtx,DWORD timeout)237 static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
238 {
239   int result, lastWaiter;
240 
241   /* Increment number of waiters */
242   EnterCriticalSection(&cond->mWaitersCountLock);
243   ++ cond->mWaitersCount;
244   LeaveCriticalSection(&cond->mWaitersCountLock);
245 
246   /* Release the mutex while waiting for the condition (will decrease
247      the number of waiters when done)... */
248   mtx_unlock(mtx);
249 
250   /* Wait for either event to become signaled due to cnd_signal() or
251      cnd_broadcast() being called */
252   result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
253   if (result == WAIT_TIMEOUT)
254   {
255     return thrd_timeout;
256   }
257   else if (result == (int)WAIT_FAILED)
258   {
259     return thrd_error;
260   }
261 
262   /* Check if we are the last waiter */
263   EnterCriticalSection(&cond->mWaitersCountLock);
264   -- cond->mWaitersCount;
265   lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
266                (cond->mWaitersCount == 0);
267   LeaveCriticalSection(&cond->mWaitersCountLock);
268 
269   /* If we are the last waiter to be notified to stop waiting, reset the event */
270   if (lastWaiter)
271   {
272     if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
273     {
274       return thrd_error;
275     }
276   }
277 
278   /* Re-acquire the mutex */
279   mtx_lock(mtx);
280 
281   return thrd_success;
282 }
283 #endif
284 
cnd_wait(cnd_t * cond,mtx_t * mtx)285 int cnd_wait(cnd_t *cond, mtx_t *mtx)
286 {
287 #if defined(_TTHREAD_WIN32_)
288   return _cnd_timedwait_win32(cond, mtx, INFINITE);
289 #else
290   return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
291 #endif
292 }
293 
cnd_timedwait(cnd_t * cond,mtx_t * mtx,const struct timespec * ts)294 int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
295 {
296 #if defined(_TTHREAD_WIN32_)
297   struct timespec now;
298   if (clock_gettime(CLOCK_REALTIME, &now) == 0)
299   {
300     DWORD delta = (DWORD) ((ts->tv_sec - now.tv_sec) * 1000 +
301                            (ts->tv_nsec - now.tv_nsec + 500000) / 1000000);
302     return _cnd_timedwait_win32(cond, mtx, delta);
303   }
304   else
305     return thrd_error;
306 #else
307   int ret;
308   ret = pthread_cond_timedwait(cond, mtx, ts);
309   if (ret == ETIMEDOUT)
310   {
311     return thrd_timeout;
312   }
313   return ret == 0 ? thrd_success : thrd_error;
314 #endif
315 }
316 
317 
318 /** Information to pass to the new thread (what to run). */
319 typedef struct {
320   thrd_start_t mFunction; /**< Pointer to the function to be executed. */
321   void * mArg;            /**< Function argument for the thread function. */
322 } _thread_start_info;
323 
324 /* Thread wrapper function. */
325 #if defined(_TTHREAD_WIN32_)
_thrd_wrapper_function(void * aArg)326 static unsigned WINAPI _thrd_wrapper_function(void * aArg)
327 #elif defined(_TTHREAD_POSIX_)
328 static void * _thrd_wrapper_function(void * aArg)
329 #endif
330 {
331   thrd_start_t fun;
332   void *arg;
333   int  res;
334 #if defined(_TTHREAD_POSIX_)
335   void *pres;
336 #endif
337 
338   /* Get thread startup information */
339   _thread_start_info *ti = (_thread_start_info *) aArg;
340   fun = ti->mFunction;
341   arg = ti->mArg;
342 
343   /* The thread is responsible for freeing the startup information */
344   free((void *)ti);
345 
346   /* Call the actual client thread function */
347   res = fun(arg);
348 
349 #if defined(_TTHREAD_WIN32_)
350   return res;
351 #else
352   pres = malloc(sizeof(int));
353   if (pres != NULL)
354   {
355     *(int*)pres = res;
356   }
357   return pres;
358 #endif
359 }
360 
thrd_create(thrd_t * thr,thrd_start_t func,void * arg)361 int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
362 {
363   /* Fill out the thread startup information (passed to the thread wrapper,
364      which will eventually free it) */
365   _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
366   if (ti == NULL)
367   {
368     return thrd_nomem;
369   }
370   ti->mFunction = func;
371   ti->mArg = arg;
372 
373   /* Create the thread */
374 #if defined(_TTHREAD_WIN32_)
375   *thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL);
376 #elif defined(_TTHREAD_POSIX_)
377   if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
378   {
379     *thr = 0;
380   }
381 #endif
382 
383   /* Did we fail to create the thread? */
384   if(!*thr)
385   {
386     free(ti);
387     return thrd_error;
388   }
389 
390   return thrd_success;
391 }
392 
thrd_current(void)393 thrd_t thrd_current(void)
394 {
395 #if defined(_TTHREAD_WIN32_)
396   return GetCurrentThread();
397 #else
398   return pthread_self();
399 #endif
400 }
401 
thrd_detach(thrd_t thr)402 int thrd_detach(thrd_t thr)
403 {
404   /* FIXME! */
405   (void)thr;
406   return thrd_error;
407 }
408 
thrd_equal(thrd_t thr0,thrd_t thr1)409 int thrd_equal(thrd_t thr0, thrd_t thr1)
410 {
411 #if defined(_TTHREAD_WIN32_)
412   return thr0 == thr1;
413 #else
414   return pthread_equal(thr0, thr1);
415 #endif
416 }
417 
thrd_exit(int res)418 void thrd_exit(int res)
419 {
420 #if defined(_TTHREAD_WIN32_)
421   ExitThread(res);
422 #else
423   void *pres = malloc(sizeof(int));
424   if (pres != NULL)
425   {
426     *(int*)pres = res;
427   }
428   pthread_exit(pres);
429 #endif
430 }
431 
thrd_join(thrd_t thr,int * res)432 int thrd_join(thrd_t thr, int *res)
433 {
434 #if defined(_TTHREAD_WIN32_)
435   if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
436   {
437     return thrd_error;
438   }
439   if (res != NULL)
440   {
441     DWORD dwRes;
442     GetExitCodeThread(thr, &dwRes);
443     *res = dwRes;
444   }
445 #elif defined(_TTHREAD_POSIX_)
446   void *pres;
447   int ires = 0;
448   if (pthread_join(thr, &pres) != 0)
449   {
450     return thrd_error;
451   }
452   if (pres != NULL)
453   {
454     ires = *(int*)pres;
455     free(pres);
456   }
457   if (res != NULL)
458   {
459     *res = ires;
460   }
461 #endif
462   return thrd_success;
463 }
464 
thrd_sleep(const struct timespec * time_point,struct timespec * remaining)465 int thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
466 {
467   struct timespec now;
468 #if defined(_TTHREAD_WIN32_)
469   DWORD delta;
470 #else
471   long delta;
472 #endif
473 
474   /* Get the current time */
475   if (clock_gettime(CLOCK_REALTIME, &now) != 0)
476     return -2;  // FIXME: Some specific error code?
477 
478 #if defined(_TTHREAD_WIN32_)
479   /* Delta in milliseconds */
480   delta = (DWORD) ((time_point->tv_sec - now.tv_sec) * 1000 +
481                    (time_point->tv_nsec - now.tv_nsec + 500000) / 1000000);
482   if (delta > 0)
483   {
484     Sleep(delta);
485   }
486 #else
487   /* Delta in microseconds */
488   delta = (time_point->tv_sec - now.tv_sec) * 1000000L +
489           (time_point->tv_nsec - now.tv_nsec + 500L) / 1000L;
490 
491   /* On some systems, the usleep argument must be < 1000000 */
492   while (delta > 999999L)
493   {
494     usleep(999999);
495     delta -= 999999L;
496   }
497   if (delta > 0L)
498   {
499     usleep((useconds_t)delta);
500   }
501 #endif
502 
503   /* We don't support waking up prematurely (yet) */
504   if (remaining)
505   {
506     remaining->tv_sec = 0;
507     remaining->tv_nsec = 0;
508   }
509   return 0;
510 }
511 
thrd_yield(void)512 void thrd_yield(void)
513 {
514 #if defined(_TTHREAD_WIN32_)
515   Sleep(0);
516 #else
517   sched_yield();
518 #endif
519 }
520 
tss_create(tss_t * key,tss_dtor_t dtor)521 int tss_create(tss_t *key, tss_dtor_t dtor)
522 {
523 #if defined(_TTHREAD_WIN32_)
524   /* FIXME: The destructor function is not supported yet... */
525   if (dtor != NULL)
526   {
527     return thrd_error;
528   }
529   *key = TlsAlloc();
530   if (*key == TLS_OUT_OF_INDEXES)
531   {
532     return thrd_error;
533   }
534 #else
535   if (pthread_key_create(key, dtor) != 0)
536   {
537     return thrd_error;
538   }
539 #endif
540   return thrd_success;
541 }
542 
tss_delete(tss_t key)543 void tss_delete(tss_t key)
544 {
545 #if defined(_TTHREAD_WIN32_)
546   TlsFree(key);
547 #else
548   pthread_key_delete(key);
549 #endif
550 }
551 
tss_get(tss_t key)552 void *tss_get(tss_t key)
553 {
554 #if defined(_TTHREAD_WIN32_)
555   return TlsGetValue(key);
556 #else
557   return pthread_getspecific(key);
558 #endif
559 }
560 
tss_set(tss_t key,void * val)561 int tss_set(tss_t key, void *val)
562 {
563 #if defined(_TTHREAD_WIN32_)
564   if (TlsSetValue(key, val) == 0)
565   {
566     return thrd_error;
567   }
568 #else
569   if (pthread_setspecific(key, val) != 0)
570   {
571     return thrd_error;
572   }
573 #endif
574   return thrd_success;
575 }
576 
577 #if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_)
_tthread_clock_gettime(clockid_t clk_id,struct timespec * ts)578 int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts)
579 {
580 #if defined(_TTHREAD_WIN32_)
581   struct _timeb tb;
582   _ftime(&tb);
583   ts->tv_sec = (time_t)tb.time;
584   ts->tv_nsec = 1000000L * (long)tb.millitm;
585 #else
586   struct timeval tv;
587   gettimeofday(&tv, NULL);
588   ts->tv_sec = (time_t)tv.tv_sec;
589   ts->tv_nsec = 1000L * (long)tv.tv_usec;
590 #endif
591   return 0;
592 }
593 #endif // _TTHREAD_EMULATE_CLOCK_GETTIME_
594 
595