Add GL_ExtensionSupported in vid_null.c because vid_shared.c relies on
[xonotic/darkplaces.git] / thread_win.c
1 #include "quakedef.h"
2 #include "thread.h"
3 #include <process.h>
4
5 int Thread_Init(void)
6 {
7 #ifdef THREADDISABLE
8         Con_Printf("Threading disabled in this build\n");
9 #endif
10         return 0;
11 }
12
13 void Thread_Shutdown(void)
14 {
15 }
16
17 qboolean Thread_HasThreads(void)
18 {
19 #ifdef THREADDISABLE
20         return false;
21 #else
22         return true;
23 #endif
24 }
25
26 void *_Thread_CreateMutex(const char *filename, int fileline)
27 {
28         void *mutex = (void *)CreateMutex(NULL, FALSE, NULL);
29 #ifdef THREADDEBUG
30         Sys_PrintfToTerminal("%p mutex create %s:%i\n" , mutex, filename, fileline);
31 #endif
32         return mutex;
33 }
34
35 void _Thread_DestroyMutex(void *mutex, const char *filename, int fileline)
36 {
37 #ifdef THREADDEBUG
38         Sys_PrintfToTerminal("%p mutex destroy %s:%i\n", mutex, filename, fileline);
39 #endif
40         CloseHandle(mutex);
41 }
42
43 int _Thread_LockMutex(void *mutex, const char *filename, int fileline)
44 {
45 #ifdef THREADDEBUG
46         Sys_PrintfToTerminal("%p mutex lock %s:%i\n"   , mutex, filename, fileline);
47 #endif
48         return (WaitForSingleObject(mutex, INFINITE) == WAIT_FAILED) ? -1 : 0;
49 }
50
51 int _Thread_UnlockMutex(void *mutex, const char *filename, int fileline)
52 {
53 #ifdef THREADDEBUG
54         Sys_PrintfToTerminal("%p mutex unlock %s:%i\n" , mutex, filename, fileline);
55 #endif
56         return (ReleaseMutex(mutex) == FALSE) ? -1 : 0;
57 }
58
59 typedef struct thread_semaphore_s
60 {
61         HANDLE semaphore;
62         volatile LONG value;
63 }
64 thread_semaphore_t;
65
66 static thread_semaphore_t *Thread_CreateSemaphore(unsigned int v)
67 {
68         thread_semaphore_t *s = (thread_semaphore_t *)calloc(sizeof(*s), 1);
69         s->semaphore = CreateSemaphore(NULL, v, 32768, NULL);
70         s->value = v;
71         return s;
72 }
73
74 static void Thread_DestroySemaphore(thread_semaphore_t *s)
75 {
76         CloseHandle(s->semaphore);
77         free(s);
78 }
79
80 static int Thread_WaitSemaphore(thread_semaphore_t *s, unsigned int msec)
81 {
82         int r = WaitForSingleObject(s->semaphore, msec);
83         if (r == WAIT_OBJECT_0)
84         {
85                 InterlockedDecrement(&s->value);
86                 return 0;
87         }
88         if (r == WAIT_TIMEOUT)
89                 return 1;
90         return -1;
91 }
92
93 static int Thread_PostSemaphore(thread_semaphore_t *s)
94 {
95         InterlockedIncrement(&s->value);
96         if (ReleaseSemaphore(s->semaphore, 1, NULL))
97                 return 0;
98         InterlockedDecrement(&s->value);
99         return -1;
100 }
101
102 typedef struct thread_cond_s
103 {
104         HANDLE mutex;
105         int waiting;
106         int signals;
107         thread_semaphore_t *sem;
108         thread_semaphore_t *done;
109 }
110 thread_cond_t;
111
112 void *_Thread_CreateCond(const char *filename, int fileline)
113 {
114         thread_cond_t *c = (thread_cond_t *)calloc(sizeof(*c), 1);
115         c->mutex = CreateMutex(NULL, FALSE, NULL);
116         c->sem = Thread_CreateSemaphore(0);
117         c->done = Thread_CreateSemaphore(0);
118         c->waiting = 0;
119         c->signals = 0;
120 #ifdef THREADDEBUG
121         Sys_PrintfToTerminal("%p cond create %s:%i\n"   , c, filename, fileline);
122 #endif
123         return c;
124 }
125
126 void _Thread_DestroyCond(void *cond, const char *filename, int fileline)
127 {
128         thread_cond_t *c = (thread_cond_t *)cond;
129 #ifdef THREADDEBUG
130         Sys_PrintfToTerminal("%p cond destroy %s:%i\n"   , cond, filename, fileline);
131 #endif
132         Thread_DestroySemaphore(c->sem);
133         Thread_DestroySemaphore(c->done);
134         CloseHandle(c->mutex);
135 }
136
137 int _Thread_CondSignal(void *cond, const char *filename, int fileline)
138 {
139         thread_cond_t *c = (thread_cond_t *)cond;
140         int n;
141 #ifdef THREADDEBUG
142         Sys_PrintfToTerminal("%p cond signal %s:%i\n"   , cond, filename, fileline);
143 #endif
144         WaitForSingleObject(c->mutex, INFINITE);
145         n = c->waiting - c->signals;
146         if (n > 0)
147         {
148                 c->signals++;
149                 Thread_PostSemaphore(c->sem);
150         }
151         ReleaseMutex(c->mutex);
152         if (n > 0)
153                 Thread_WaitSemaphore(c->done, INFINITE);
154         return 0;
155 }
156
157 int _Thread_CondBroadcast(void *cond, const char *filename, int fileline)
158 {
159         thread_cond_t *c = (thread_cond_t *)cond;
160         int i = 0;
161         int n = 0;
162 #ifdef THREADDEBUG
163         Sys_PrintfToTerminal("%p cond broadcast %s:%i\n"   , cond, filename, fileline);
164 #endif
165         WaitForSingleObject(c->mutex, INFINITE);
166         n = c->waiting - c->signals;
167         if (n > 0)
168         {
169                 c->signals += n;
170                 for (i = 0;i < n;i++)
171                         Thread_PostSemaphore(c->sem);
172         }
173         ReleaseMutex(c->mutex);
174         for (i = 0;i < n;i++)
175                 Thread_WaitSemaphore(c->done, INFINITE);
176         return 0;
177 }
178
179 int _Thread_CondWait(void *cond, void *mutex, const char *filename, int fileline)
180 {
181         thread_cond_t *c = (thread_cond_t *)cond;
182         int waitresult;
183 #ifdef THREADDEBUG
184         Sys_PrintfToTerminal("%p cond wait %s:%i\n"   , cond, filename, fileline);
185 #endif
186
187         WaitForSingleObject(c->mutex, INFINITE);
188         c->waiting++;
189         ReleaseMutex(c->mutex);
190
191         ReleaseMutex(mutex);
192
193         waitresult = Thread_WaitSemaphore(c->sem, INFINITE);
194         WaitForSingleObject(c->mutex, INFINITE);
195         if (c->signals > 0)
196         {
197                 if (waitresult > 0)
198                         Thread_WaitSemaphore(c->sem, INFINITE);
199                 Thread_PostSemaphore(c->done);
200                 c->signals--;
201         }
202         c->waiting--;
203         ReleaseMutex(c->mutex);
204
205         WaitForSingleObject(mutex, INFINITE);
206         return waitresult;
207 }
208
209 typedef struct threadwrapper_s
210 {
211         HANDLE handle;
212         unsigned int threadid;
213         int result;
214         int (*fn)(void *);
215         void *data;
216 }
217 threadwrapper_t;
218
219 unsigned int __stdcall Thread_WrapperFunc(void *d)
220 {
221         threadwrapper_t *w = (threadwrapper_t *)d;
222         w->result = w->fn(w->data);
223         _endthreadex(w->result);
224         return w->result;
225 }
226
227 void *_Thread_CreateThread(int (*fn)(void *), void *data, const char *filename, int fileline)
228 {
229         threadwrapper_t *w = (threadwrapper_t *)calloc(sizeof(*w), 1);
230 #ifdef THREADDEBUG
231         Sys_PrintfToTerminal("%p thread create %s:%i\n"   , w, filename, fileline);
232 #endif
233         w->fn = fn;
234         w->data = data;
235         w->threadid = 0;
236         w->result = 0;
237         w->handle = (HANDLE)_beginthreadex(NULL, 0, Thread_WrapperFunc, (void *)w, 0, &w->threadid);
238         return (void *)w;
239 }
240
241 int _Thread_WaitThread(void *d, int retval, const char *filename, int fileline)
242 {
243         threadwrapper_t *w = (threadwrapper_t *)d;
244 #ifdef THREADDEBUG
245         Sys_PrintfToTerminal("%p thread wait %s:%i\n"   , w, filename, fileline);
246 #endif
247         WaitForSingleObject(w->handle, INFINITE);
248         CloseHandle(w->handle);
249         retval = w->result;
250         free(w);
251         return retval;
252 }
253
254 // standard barrier implementation using conds and mutexes
255 // see: http://www.howforge.com/implementing-barrier-in-pthreads
256 typedef struct {
257         unsigned int needed;
258         unsigned int called;
259         void *mutex;
260         void *cond;
261 } barrier_t;
262
263 void *_Thread_CreateBarrier(unsigned int count, const char *filename, int fileline)
264 {
265         volatile barrier_t *b = (volatile barrier_t *) Z_Malloc(sizeof(barrier_t));
266 #ifdef THREADDEBUG
267         Sys_PrintfToTerminal("%p barrier create(%d) %s:%i\n", b, count, filename, fileline);
268 #endif
269         b->needed = count;
270         b->called = 0;
271         b->mutex = Thread_CreateMutex();
272         b->cond = Thread_CreateCond();
273         return (void *) b;
274 }
275
276 void _Thread_DestroyBarrier(void *barrier, const char *filename, int fileline)
277 {
278         volatile barrier_t *b = (volatile barrier_t *) barrier;
279 #ifdef THREADDEBUG
280         Sys_PrintfToTerminal("%p barrier destroy %s:%i\n", b, filename, fileline);
281 #endif
282         Thread_DestroyMutex(b->mutex);
283         Thread_DestroyCond(b->cond);
284 }
285
286 void _Thread_WaitBarrier(void *barrier, const char *filename, int fileline)
287 {
288         volatile barrier_t *b = (volatile barrier_t *) barrier;
289 #ifdef THREADDEBUG
290         Sys_PrintfToTerminal("%p barrier wait %s:%i\n", b, filename, fileline);
291 #endif
292         Thread_LockMutex(b->mutex);
293         b->called++;
294         if (b->called == b->needed) {
295                 b->called = 0;
296                 Thread_CondBroadcast(b->cond);
297         } else {
298                 do {
299                         Thread_CondWait(b->cond, b->mutex);
300                 } while(b->called);
301         }
302         Thread_UnlockMutex(b->mutex);
303 }