zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

tinycthread.c (13065B)


      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 Löwy <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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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_)
    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 
    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 
    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_)
    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 
    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 
    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 
    402 int thrd_detach(thrd_t thr)
    403 {
    404   /* FIXME! */
    405   (void)thr;
    406   return thrd_error;
    407 }
    408 
    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 
    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 
    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 
    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 
    512 void thrd_yield(void)
    513 {
    514 #if defined(_TTHREAD_WIN32_)
    515   Sleep(0);
    516 #else
    517   sched_yield();
    518 #endif
    519 }
    520 
    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 
    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 
    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 
    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_)
    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