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