Merge branch 'rs/status-code-clean-up'
[git] / compat / win32 / pthread.c
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
16 static 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
24 int 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
38 int 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
53 pthread_t pthread_self(void)
54 {
55         pthread_t t = { NULL };
56         t.tid = GetCurrentThreadId();
57         return t;
58 }
59
60 int 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
80 int 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
88 int 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  */
146 int 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  */
169 int 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 }