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