Home | History | Annotate | Download | only in c11
      1 /*
      2  * C11 <threads.h> emulation library
      3  *
      4  * (C) Copyright yohhoy 2012.
      5  * Distributed under the Boost Software License, Version 1.0.
      6  *
      7  * Permission is hereby granted, free of charge, to any person or organization
      8  * obtaining a copy of the software and accompanying documentation covered by
      9  * this license (the "Software") to use, reproduce, display, distribute,
     10  * execute, and transmit the Software, and to prepare [[derivative work]]s of the
     11  * Software, and to permit third-parties to whom the Software is furnished to
     12  * do so, all subject to the following:
     13  *
     14  * The copyright notices in the Software and this entire statement, including
     15  * the above license grant, this restriction and the following disclaimer,
     16  * must be included in all copies of the Software, in whole or in part, and
     17  * all derivative works of the Software, unless such copies or derivative
     18  * works are solely in the form of machine-executable object code generated by
     19  * a source language processor.
     20  *
     21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     23  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
     24  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
     25  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
     26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     27  * DEALINGS IN THE SOFTWARE.
     28  */
     29 #ifndef assert
     30 #include <assert.h>
     31 #endif
     32 #include <limits.h>
     33 #include <errno.h>
     34 #include <process.h>  // MSVCRT
     35 #include <stdlib.h>
     36 
     37 /*
     38 Configuration macro:
     39 
     40   EMULATED_THREADS_USE_NATIVE_CALL_ONCE
     41     Use native WindowsAPI one-time initialization function.
     42     (requires WinVista or later)
     43     Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
     44 
     45   EMULATED_THREADS_USE_NATIVE_CV
     46     Use native WindowsAPI condition variable object.
     47     (requires WinVista or later)
     48     Otherwise use emulated implementation for WinXP.
     49 
     50   EMULATED_THREADS_TSS_DTOR_SLOTNUM
     51     Max registerable TSS dtor number.
     52 */
     53 
     54 // XXX: Retain XP compatability
     55 #if 0
     56 #if _WIN32_WINNT >= 0x0600
     57 // Prefer native WindowsAPI on newer environment.
     58 #if !defined(__MINGW32__)
     59 #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
     60 #endif
     61 #define EMULATED_THREADS_USE_NATIVE_CV
     62 #endif
     63 #endif
     64 #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64  // see TLS_MINIMUM_AVAILABLE
     65 
     66 
     67 #include <windows.h>
     68 
     69 // check configuration
     70 #if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600)
     71 #error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
     72 #endif
     73 
     74 #if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600)
     75 #error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600
     76 #endif
     77 
     78 
     79 /*---------------------------- macros ----------------------------*/
     80 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
     81 #define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
     82 #else
     83 #define ONCE_FLAG_INIT {0}
     84 #endif
     85 #define TSS_DTOR_ITERATIONS 1
     86 
     87 // FIXME: temporary non-standard hack to ease transition
     88 #define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
     89 
     90 /*---------------------------- types ----------------------------*/
     91 typedef struct cnd_t {
     92 #ifdef EMULATED_THREADS_USE_NATIVE_CV
     93     CONDITION_VARIABLE condvar;
     94 #else
     95     int blocked;
     96     int gone;
     97     int to_unblock;
     98     HANDLE sem_queue;
     99     HANDLE sem_gate;
    100     CRITICAL_SECTION monitor;
    101 #endif
    102 } cnd_t;
    103 
    104 typedef HANDLE thrd_t;
    105 
    106 typedef DWORD tss_t;
    107 
    108 typedef CRITICAL_SECTION mtx_t;
    109 
    110 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
    111 typedef INIT_ONCE once_flag;
    112 #else
    113 typedef struct once_flag_t {
    114     volatile LONG status;
    115 } once_flag;
    116 #endif
    117 
    118 
    119 static inline void * tss_get(tss_t key);
    120 static inline void thrd_yield(void);
    121 static inline int mtx_trylock(mtx_t *mtx);
    122 static inline int mtx_lock(mtx_t *mtx);
    123 static inline int mtx_unlock(mtx_t *mtx);
    124 
    125 /*
    126 Implementation limits:
    127   - Conditionally emulation for "Initialization functions"
    128     (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
    129   - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
    130 */
    131 static void impl_tss_dtor_invoke(void);  // forward decl.
    132 
    133 struct impl_thrd_param {
    134     thrd_start_t func;
    135     void *arg;
    136 };
    137 
    138 static unsigned __stdcall impl_thrd_routine(void *p)
    139 {
    140     struct impl_thrd_param pack;
    141     int code;
    142     memcpy(&pack, p, sizeof(struct impl_thrd_param));
    143     free(p);
    144     code = pack.func(pack.arg);
    145     impl_tss_dtor_invoke();
    146     return (unsigned)code;
    147 }
    148 
    149 static DWORD impl_xtime2msec(const xtime *xt)
    150 {
    151     return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L));
    152 }
    153 
    154 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
    155 struct impl_call_once_param { void (*func)(void); };
    156 static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
    157 {
    158     struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
    159     (param->func)();
    160     ((void)InitOnce); ((void)Context);  // suppress warning
    161     return TRUE;
    162 }
    163 #endif  // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
    164 
    165 #ifndef EMULATED_THREADS_USE_NATIVE_CV
    166 /*
    167 Note:
    168   The implementation of condition variable is ported from Boost.Interprocess
    169   See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
    170 */
    171 static void impl_cond_do_signal(cnd_t *cond, int broadcast)
    172 {
    173     int nsignal = 0;
    174 
    175     EnterCriticalSection(&cond->monitor);
    176     if (cond->to_unblock != 0) {
    177         if (cond->blocked == 0) {
    178             LeaveCriticalSection(&cond->monitor);
    179             return;
    180         }
    181         if (broadcast) {
    182             cond->to_unblock += nsignal = cond->blocked;
    183             cond->blocked = 0;
    184         } else {
    185             nsignal = 1;
    186             cond->to_unblock++;
    187             cond->blocked--;
    188         }
    189     } else if (cond->blocked > cond->gone) {
    190         WaitForSingleObject(cond->sem_gate, INFINITE);
    191         if (cond->gone != 0) {
    192             cond->blocked -= cond->gone;
    193             cond->gone = 0;
    194         }
    195         if (broadcast) {
    196             nsignal = cond->to_unblock = cond->blocked;
    197             cond->blocked = 0;
    198         } else {
    199             nsignal = cond->to_unblock = 1;
    200             cond->blocked--;
    201         }
    202     }
    203     LeaveCriticalSection(&cond->monitor);
    204 
    205     if (0 < nsignal)
    206         ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
    207 }
    208 
    209 static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
    210 {
    211     int nleft = 0;
    212     int ngone = 0;
    213     int timeout = 0;
    214     DWORD w;
    215 
    216     WaitForSingleObject(cond->sem_gate, INFINITE);
    217     cond->blocked++;
    218     ReleaseSemaphore(cond->sem_gate, 1, NULL);
    219 
    220     mtx_unlock(mtx);
    221 
    222     w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE);
    223     timeout = (w == WAIT_TIMEOUT);
    224 
    225     EnterCriticalSection(&cond->monitor);
    226     if ((nleft = cond->to_unblock) != 0) {
    227         if (timeout) {
    228             if (cond->blocked != 0) {
    229                 cond->blocked--;
    230             } else {
    231                 cond->gone++;
    232             }
    233         }
    234         if (--cond->to_unblock == 0) {
    235             if (cond->blocked != 0) {
    236                 ReleaseSemaphore(cond->sem_gate, 1, NULL);
    237                 nleft = 0;
    238             }
    239             else if ((ngone = cond->gone) != 0) {
    240                 cond->gone = 0;
    241             }
    242         }
    243     } else if (++cond->gone == INT_MAX/2) {
    244         WaitForSingleObject(cond->sem_gate, INFINITE);
    245         cond->blocked -= cond->gone;
    246         ReleaseSemaphore(cond->sem_gate, 1, NULL);
    247         cond->gone = 0;
    248     }
    249     LeaveCriticalSection(&cond->monitor);
    250 
    251     if (nleft == 1) {
    252         while (ngone--)
    253             WaitForSingleObject(cond->sem_queue, INFINITE);
    254         ReleaseSemaphore(cond->sem_gate, 1, NULL);
    255     }
    256 
    257     mtx_lock(mtx);
    258     return timeout ? thrd_busy : thrd_success;
    259 }
    260 #endif  // ifndef EMULATED_THREADS_USE_NATIVE_CV
    261 
    262 static struct impl_tss_dtor_entry {
    263     tss_t key;
    264     tss_dtor_t dtor;
    265 } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
    266 
    267 static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
    268 {
    269     int i;
    270     for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
    271         if (!impl_tss_dtor_tbl[i].dtor)
    272             break;
    273     }
    274     if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
    275         return 1;
    276     impl_tss_dtor_tbl[i].key = key;
    277     impl_tss_dtor_tbl[i].dtor = dtor;
    278     return 0;
    279 }
    280 
    281 static void impl_tss_dtor_invoke()
    282 {
    283     int i;
    284     for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
    285         if (impl_tss_dtor_tbl[i].dtor) {
    286             void* val = tss_get(impl_tss_dtor_tbl[i].key);
    287             if (val)
    288                 (impl_tss_dtor_tbl[i].dtor)(val);
    289         }
    290     }
    291 }
    292 
    293 
    294 /*--------------- 7.25.2 Initialization functions ---------------*/
    295 // 7.25.2.1
    296 static inline void
    297 call_once(once_flag *flag, void (*func)(void))
    298 {
    299     assert(flag && func);
    300 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
    301     {
    302     struct impl_call_once_param param;
    303     param.func = func;
    304     InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)&param, NULL);
    305     }
    306 #else
    307     if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
    308         (func)();
    309         InterlockedExchange(&flag->status, 2);
    310     } else {
    311         while (flag->status == 1) {
    312             // busy loop!
    313             thrd_yield();
    314         }
    315     }
    316 #endif
    317 }
    318 
    319 
    320 /*------------- 7.25.3 Condition variable functions -------------*/
    321 // 7.25.3.1
    322 static inline int
    323 cnd_broadcast(cnd_t *cond)
    324 {
    325     if (!cond) return thrd_error;
    326 #ifdef EMULATED_THREADS_USE_NATIVE_CV
    327     WakeAllConditionVariable(&cond->condvar);
    328 #else
    329     impl_cond_do_signal(cond, 1);
    330 #endif
    331     return thrd_success;
    332 }
    333 
    334 // 7.25.3.2
    335 static inline void
    336 cnd_destroy(cnd_t *cond)
    337 {
    338     assert(cond);
    339 #ifdef EMULATED_THREADS_USE_NATIVE_CV
    340     // do nothing
    341 #else
    342     CloseHandle(cond->sem_queue);
    343     CloseHandle(cond->sem_gate);
    344     DeleteCriticalSection(&cond->monitor);
    345 #endif
    346 }
    347 
    348 // 7.25.3.3
    349 static inline int
    350 cnd_init(cnd_t *cond)
    351 {
    352     if (!cond) return thrd_error;
    353 #ifdef EMULATED_THREADS_USE_NATIVE_CV
    354     InitializeConditionVariable(&cond->condvar);
    355 #else
    356     cond->blocked = 0;
    357     cond->gone = 0;
    358     cond->to_unblock = 0;
    359     cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
    360     cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL);
    361     InitializeCriticalSection(&cond->monitor);
    362 #endif
    363     return thrd_success;
    364 }
    365 
    366 // 7.25.3.4
    367 static inline int
    368 cnd_signal(cnd_t *cond)
    369 {
    370     if (!cond) return thrd_error;
    371 #ifdef EMULATED_THREADS_USE_NATIVE_CV
    372     WakeConditionVariable(&cond->condvar);
    373 #else
    374     impl_cond_do_signal(cond, 0);
    375 #endif
    376     return thrd_success;
    377 }
    378 
    379 // 7.25.3.5
    380 static inline int
    381 cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
    382 {
    383     if (!cond || !mtx || !xt) return thrd_error;
    384 #ifdef EMULATED_THREADS_USE_NATIVE_CV
    385     if (SleepConditionVariableCS(&cond->condvar, mtx, impl_xtime2msec(xt)))
    386         return thrd_success;
    387     return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
    388 #else
    389     return impl_cond_do_wait(cond, mtx, xt);
    390 #endif
    391 }
    392 
    393 // 7.25.3.6
    394 static inline int
    395 cnd_wait(cnd_t *cond, mtx_t *mtx)
    396 {
    397     if (!cond || !mtx) return thrd_error;
    398 #ifdef EMULATED_THREADS_USE_NATIVE_CV
    399     SleepConditionVariableCS(&cond->condvar, mtx, INFINITE);
    400 #else
    401     impl_cond_do_wait(cond, mtx, NULL);
    402 #endif
    403     return thrd_success;
    404 }
    405 
    406 
    407 /*-------------------- 7.25.4 Mutex functions --------------------*/
    408 // 7.25.4.1
    409 static inline void
    410 mtx_destroy(mtx_t *mtx)
    411 {
    412     assert(mtx);
    413     DeleteCriticalSection(mtx);
    414 }
    415 
    416 // 7.25.4.2
    417 static inline int
    418 mtx_init(mtx_t *mtx, int type)
    419 {
    420     if (!mtx) return thrd_error;
    421     if (type != mtx_plain && type != mtx_timed && type != mtx_try
    422       && type != (mtx_plain|mtx_recursive)
    423       && type != (mtx_timed|mtx_recursive)
    424       && type != (mtx_try|mtx_recursive))
    425         return thrd_error;
    426     InitializeCriticalSection(mtx);
    427     return thrd_success;
    428 }
    429 
    430 // 7.25.4.3
    431 static inline int
    432 mtx_lock(mtx_t *mtx)
    433 {
    434     if (!mtx) return thrd_error;
    435     EnterCriticalSection(mtx);
    436     return thrd_success;
    437 }
    438 
    439 // 7.25.4.4
    440 static inline int
    441 mtx_timedlock(mtx_t *mtx, const xtime *xt)
    442 {
    443     time_t expire, now;
    444     if (!mtx || !xt) return thrd_error;
    445     expire = time(NULL);
    446     expire += xt->sec;
    447     while (mtx_trylock(mtx) != thrd_success) {
    448         now = time(NULL);
    449         if (expire < now)
    450             return thrd_busy;
    451         // busy loop!
    452         thrd_yield();
    453     }
    454     return thrd_success;
    455 }
    456 
    457 // 7.25.4.5
    458 static inline int
    459 mtx_trylock(mtx_t *mtx)
    460 {
    461     if (!mtx) return thrd_error;
    462     return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
    463 }
    464 
    465 // 7.25.4.6
    466 static inline int
    467 mtx_unlock(mtx_t *mtx)
    468 {
    469     if (!mtx) return thrd_error;
    470     LeaveCriticalSection(mtx);
    471     return thrd_success;
    472 }
    473 
    474 
    475 /*------------------- 7.25.5 Thread functions -------------------*/
    476 // 7.25.5.1
    477 static inline int
    478 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
    479 {
    480     struct impl_thrd_param *pack;
    481     uintptr_t handle;
    482     if (!thr) return thrd_error;
    483     pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
    484     if (!pack) return thrd_nomem;
    485     pack->func = func;
    486     pack->arg = arg;
    487     handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
    488     if (handle == 0) {
    489         if (errno == EAGAIN || errno == EACCES)
    490             return thrd_nomem;
    491         return thrd_error;
    492     }
    493     *thr = (thrd_t)handle;
    494     return thrd_success;
    495 }
    496 
    497 #if 0
    498 // 7.25.5.2
    499 static inline thrd_t
    500 thrd_current(void)
    501 {
    502     HANDLE hCurrentThread;
    503     BOOL bRet;
    504 
    505     /* GetCurrentThread() returns a pseudo-handle, which is useless.  We need
    506      * to call DuplicateHandle to get a real handle.  However the handle value
    507      * will not match the one returned by thread_create.
    508      *
    509      * Other potential solutions would be:
    510      * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
    511      * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
    512      *
    513      * Neither is particularly nice.
    514      *
    515      * Life would be much easier if C11 threads had different abstractions for
    516      * threads and thread IDs, just like C++11 threads does...
    517      */
    518 
    519     bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
    520                            GetCurrentThread(), // source (pseudo) handle
    521                            GetCurrentProcess(), // target process
    522                            &hCurrentThread, // target handle
    523                            0,
    524                            FALSE,
    525                            DUPLICATE_SAME_ACCESS);
    526     assert(bRet);
    527     if (!bRet) {
    528 	hCurrentThread = GetCurrentThread();
    529     }
    530     return hCurrentThread;
    531 }
    532 #endif
    533 
    534 // 7.25.5.3
    535 static inline int
    536 thrd_detach(thrd_t thr)
    537 {
    538     CloseHandle(thr);
    539     return thrd_success;
    540 }
    541 
    542 // 7.25.5.4
    543 static inline int
    544 thrd_equal(thrd_t thr0, thrd_t thr1)
    545 {
    546     return GetThreadId(thr0) == GetThreadId(thr1);
    547 }
    548 
    549 // 7.25.5.5
    550 static inline void
    551 thrd_exit(int res)
    552 {
    553     impl_tss_dtor_invoke();
    554     _endthreadex((unsigned)res);
    555 }
    556 
    557 // 7.25.5.6
    558 static inline int
    559 thrd_join(thrd_t thr, int *res)
    560 {
    561     DWORD w, code;
    562     w = WaitForSingleObject(thr, INFINITE);
    563     if (w != WAIT_OBJECT_0)
    564         return thrd_error;
    565     if (res) {
    566         if (!GetExitCodeThread(thr, &code)) {
    567             CloseHandle(thr);
    568             return thrd_error;
    569         }
    570         *res = (int)code;
    571     }
    572     CloseHandle(thr);
    573     return thrd_success;
    574 }
    575 
    576 // 7.25.5.7
    577 static inline void
    578 thrd_sleep(const xtime *xt)
    579 {
    580     assert(xt);
    581     Sleep(impl_xtime2msec(xt));
    582 }
    583 
    584 // 7.25.5.8
    585 static inline void
    586 thrd_yield(void)
    587 {
    588     SwitchToThread();
    589 }
    590 
    591 
    592 /*----------- 7.25.6 Thread-specific storage functions -----------*/
    593 // 7.25.6.1
    594 static inline int
    595 tss_create(tss_t *key, tss_dtor_t dtor)
    596 {
    597     if (!key) return thrd_error;
    598     *key = TlsAlloc();
    599     if (dtor) {
    600         if (impl_tss_dtor_register(*key, dtor)) {
    601             TlsFree(*key);
    602             return thrd_error;
    603         }
    604     }
    605     return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
    606 }
    607 
    608 // 7.25.6.2
    609 static inline void
    610 tss_delete(tss_t key)
    611 {
    612     TlsFree(key);
    613 }
    614 
    615 // 7.25.6.3
    616 static inline void *
    617 tss_get(tss_t key)
    618 {
    619     return TlsGetValue(key);
    620 }
    621 
    622 // 7.25.6.4
    623 static inline int
    624 tss_set(tss_t key, void *val)
    625 {
    626     return TlsSetValue(key, val) ? thrd_success : thrd_error;
    627 }
    628 
    629 
    630 /*-------------------- 7.25.7 Time functions --------------------*/
    631 // 7.25.6.1
    632 static inline int
    633 xtime_get(xtime *xt, int base)
    634 {
    635     if (!xt) return 0;
    636     if (base == TIME_UTC) {
    637         xt->sec = time(NULL);
    638         xt->nsec = 0;
    639         return base;
    640     }
    641     return 0;
    642 }
    643