compat / win32 / pthread.con commit t3404: todo list with commented-out commands only aborts (d078c39)
   1/*
   2 * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
   3 *
   4 * DISCLAIMER: The implementation is Git-specific, it is subset of original
   5 * Pthreads API, without lots of other features that Git doesn't use.
   6 * Git also makes sure that the passed arguments are valid, so there's
   7 * no need for double-checking.
   8 */
   9
  10#include "../../git-compat-util.h"
  11#include "pthread.h"
  12
  13#include <errno.h>
  14#include <limits.h>
  15
  16static unsigned __stdcall win32_start_routine(void *arg)
  17{
  18        pthread_t *thread = arg;
  19        thread->tid = GetCurrentThreadId();
  20        thread->arg = thread->start_routine(thread->arg);
  21        return 0;
  22}
  23
  24int pthread_create(pthread_t *thread, const void *unused,
  25                   void *(*start_routine)(void*), void *arg)
  26{
  27        thread->arg = arg;
  28        thread->start_routine = start_routine;
  29        thread->handle = (HANDLE)
  30                _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
  31
  32        if (!thread->handle)
  33                return errno;
  34        else
  35                return 0;
  36}
  37
  38int win32_pthread_join(pthread_t *thread, void **value_ptr)
  39{
  40        DWORD result = WaitForSingleObject(thread->handle, INFINITE);
  41        switch (result) {
  42                case WAIT_OBJECT_0:
  43                        if (value_ptr)
  44                                *value_ptr = thread->arg;
  45                        return 0;
  46                case WAIT_ABANDONED:
  47                        return EINVAL;
  48                default:
  49                        return err_win_to_posix(GetLastError());
  50        }
  51}
  52
  53pthread_t pthread_self(void)
  54{
  55        pthread_t t = { NULL };
  56        t.tid = GetCurrentThreadId();
  57        return t;
  58}
  59
  60int pthread_cond_init(pthread_cond_t *cond, const void *unused)
  61{
  62        cond->waiters = 0;
  63        cond->was_broadcast = 0;
  64        InitializeCriticalSection(&cond->waiters_lock);
  65
  66        cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
  67        if (!cond->sema)
  68                die("CreateSemaphore() failed");
  69
  70        cond->continue_broadcast = CreateEvent(NULL,    /* security */
  71                                FALSE,                  /* auto-reset */
  72                                FALSE,                  /* not signaled */
  73                                NULL);                  /* name */
  74        if (!cond->continue_broadcast)
  75                die("CreateEvent() failed");
  76
  77        return 0;
  78}
  79
  80int pthread_cond_destroy(pthread_cond_t *cond)
  81{
  82        CloseHandle(cond->sema);
  83        CloseHandle(cond->continue_broadcast);
  84        DeleteCriticalSection(&cond->waiters_lock);
  85        return 0;
  86}
  87
  88int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
  89{
  90        int last_waiter;
  91
  92        EnterCriticalSection(&cond->waiters_lock);
  93        cond->waiters++;
  94        LeaveCriticalSection(&cond->waiters_lock);
  95
  96        /*
  97         * Unlock external mutex and wait for signal.
  98         * NOTE: we've held mutex locked long enough to increment
  99         * waiters count above, so there's no problem with
 100         * leaving mutex unlocked before we wait on semaphore.
 101         */
 102        LeaveCriticalSection(mutex);
 103
 104        /* let's wait - ignore return value */
 105        WaitForSingleObject(cond->sema, INFINITE);
 106
 107        /*
 108         * Decrease waiters count. If we are the last waiter, then we must
 109         * notify the broadcasting thread that it can continue.
 110         * But if we continued due to cond_signal, we do not have to do that
 111         * because the signaling thread knows that only one waiter continued.
 112         */
 113        EnterCriticalSection(&cond->waiters_lock);
 114        cond->waiters--;
 115        last_waiter = cond->was_broadcast && cond->waiters == 0;
 116        LeaveCriticalSection(&cond->waiters_lock);
 117
 118        if (last_waiter) {
 119                /*
 120                 * cond_broadcast was issued while mutex was held. This means
 121                 * that all other waiters have continued, but are contending
 122                 * for the mutex at the end of this function because the
 123                 * broadcasting thread did not leave cond_broadcast, yet.
 124                 * (This is so that it can be sure that each waiter has
 125                 * consumed exactly one slice of the semaphor.)
 126                 * The last waiter must tell the broadcasting thread that it
 127                 * can go on.
 128                 */
 129                SetEvent(cond->continue_broadcast);
 130                /*
 131                 * Now we go on to contend with all other waiters for
 132                 * the mutex. Auf in den Kampf!
 133                 */
 134        }
 135        /* lock external mutex again */
 136        EnterCriticalSection(mutex);
 137
 138        return 0;
 139}
 140
 141/*
 142 * IMPORTANT: This implementation requires that pthread_cond_signal
 143 * is called while the mutex is held that is used in the corresponding
 144 * pthread_cond_wait calls!
 145 */
 146int pthread_cond_signal(pthread_cond_t *cond)
 147{
 148        int have_waiters;
 149
 150        EnterCriticalSection(&cond->waiters_lock);
 151        have_waiters = cond->waiters > 0;
 152        LeaveCriticalSection(&cond->waiters_lock);
 153
 154        /*
 155         * Signal only when there are waiters
 156         */
 157        if (have_waiters)
 158                return ReleaseSemaphore(cond->sema, 1, NULL) ?
 159                        0 : err_win_to_posix(GetLastError());
 160        else
 161                return 0;
 162}
 163
 164/*
 165 * DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast
 166 * is called while the mutex is held that is used in the corresponding
 167 * pthread_cond_wait calls!
 168 */
 169int pthread_cond_broadcast(pthread_cond_t *cond)
 170{
 171        EnterCriticalSection(&cond->waiters_lock);
 172
 173        if ((cond->was_broadcast = cond->waiters > 0)) {
 174                /* wake up all waiters */
 175                ReleaseSemaphore(cond->sema, cond->waiters, NULL);
 176                LeaveCriticalSection(&cond->waiters_lock);
 177                /*
 178                 * At this point all waiters continue. Each one takes its
 179                 * slice of the semaphor. Now it's our turn to wait: Since
 180                 * the external mutex is held, no thread can leave cond_wait,
 181                 * yet. For this reason, we can be sure that no thread gets
 182                 * a chance to eat *more* than one slice. OTOH, it means
 183                 * that the last waiter must send us a wake-up.
 184                 */
 185                WaitForSingleObject(cond->continue_broadcast, INFINITE);
 186                /*
 187                 * Since the external mutex is held, no thread can enter
 188                 * cond_wait, and, hence, it is safe to reset this flag
 189                 * without cond->waiters_lock held.
 190                 */
 191                cond->was_broadcast = 0;
 192        } else {
 193                LeaveCriticalSection(&cond->waiters_lock);
 194        }
 195        return 0;
 196}