15b1ca83d98424a86cb41ec949a4c1a5abd0c1c1
[silc.git] / lib / silcutil / win32 / silcwin32thread.c
1 /*
2
3   silcwin32thread.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /**************************** SILC Thread API *******************************/
24
25 #ifdef SILC_THREADS
26
27 /* Thread structure for WIN32 */
28 typedef struct {
29   HANDLE thread;
30   SilcThreadStart start_func;
31   void *context;
32   SilcBool waitable;
33 } *SilcWin32Thread;
34
35 static DWORD silc_thread_tls;
36
37 /* Actual routine that is called by WIN32 when the thread is created.
38    We will call the start_func from here. When this returns the thread
39    is destroyed. */
40
41 unsigned __stdcall silc_thread_win32_start(void *context)
42 {
43   SilcWin32Thread thread = (SilcWin32Thread)context;
44
45   silc_thread_tls = TlsAlloc();
46   if (silc_thread_tls != TLS_OUT_OF_INDEXES)
47     TlsSetValue(silc_thread_tls, context);
48
49   silc_thread_exit(thread->start_func(thread->context));
50
51   return 0;
52 }
53 #endif
54
55 SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
56                               SilcBool waitable)
57 {
58 #ifdef SILC_THREADS
59   SilcWin32Thread thread;
60   unsigned id;
61
62   SILC_LOG_DEBUG(("Creating new thread"));
63
64   thread = silc_calloc(1, sizeof(*thread));
65   thread->start_func = start_func;
66   thread->context = context;
67   thread->waitable = waitable;
68   thread->thread =
69     _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE)silc_thread_win32_start,
70                    (void *)thread, 0, &id);
71
72   if (!thread->thread) {
73     SILC_LOG_ERROR(("Could not create new thread"));
74     silc_free(thread);
75     return NULL;
76   }
77
78   return (SilcThread)thread;
79 #else
80   /* Call thread callback immediately */
81   (*start_func)(context);
82   return NULL;
83 #endif
84 }
85
86 void silc_thread_exit(void *exit_value)
87 {
88 #ifdef SILC_THREADS
89   SilcWin32Thread thread = TlsGetValue(silc_thread_tls);
90
91   if (thread) {
92     /* If the thread is waitable the memory is freed only in silc_thread_wait
93        by another thread. If not waitable, free it now. */
94     if (!thread->waitable)
95       silc_free(thread);
96   }
97
98   TlsFree(silc_thread_tls);
99   _endthreadex(0);
100 #endif
101 }
102
103 SilcThread silc_thread_self(void)
104 {
105 #ifdef SILC_THREADS
106   SilcWin32Thread self = TlsGetValue(silc_thread_tls);
107
108   if (!self) {
109     /* This should only happen for the main thread. */
110     HANDLE handle = GetCurrentThread ();
111     HANDLE process = GetCurrentProcess ();
112     self = silc_calloc(1, sizeof(*self));
113     DuplicateHandle(process, handle, process,
114                     &self->thread, 0, FALSE,
115                     DUPLICATE_SAME_ACCESS);
116     TlsSetValue(silc_thread_tls, self);
117   }
118
119   return (SilcThread)self;
120 #else
121   return NULL;
122 #endif
123 }
124
125 SilcBool silc_thread_wait(SilcThread thread, void **exit_value)
126 {
127 #ifdef SILC_THREADS
128   SilcWin32Thread self = (SilcWin32Thread)thread;
129
130   SILC_LOG_DEBUG(("Waiting for thread %p", self));
131
132   if (!self->waitable)
133     return FALSE;
134
135   /* The thread is waitable thus we will free all memory after the
136      WaitForSingleObject returns, the thread is destroyed after that. */
137   WaitForSingleObject(self->thread, INFINITE);
138   CloseHandle(self->thread);
139
140   if (exit_value)
141     *exit_value = NULL;
142
143   return TRUE;
144 #else
145   return FALSE;
146 #endif
147 }
148
149 void silc_thread_yield(void)
150 {
151 #ifdef SILC_THREADS
152   SleepEx (0,0);
153 #endif /* SILC_THREADS */
154 }
155
156
157 /***************************** SILC Mutex API *******************************/
158
159 /* SILC Mutex structure */
160 struct SilcMutexStruct {
161 #ifdef SILC_THREADS
162   CRITICAL_SECTION mutex;
163 #endif /* SILC_THREADS */
164   unsigned int locked : 1;
165 };
166
167 SilcBool silc_mutex_alloc(SilcMutex *mutex)
168 {
169 #ifdef SILC_THREADS
170   *mutex = silc_calloc(1, sizeof(**mutex));
171   if (!(*mutex))
172     return FALSE;
173   InitializeCriticalSection(&((*mutex)->mutex));
174   return TRUE;
175 #else
176   return FALSE;
177 #endif /* SILC_THREADS */
178 }
179
180 void silc_mutex_free(SilcMutex mutex)
181 {
182 #ifdef SILC_THREADS
183   if (mutex) {
184     DeleteCriticalSection(&mutex->mutex);
185     silc_free(mutex);
186   }
187 #endif /* SILC_THREADS */
188 }
189
190 void silc_mutex_lock(SilcMutex mutex)
191 {
192 #ifdef SILC_THREADS
193   if (mutex) {
194     EnterCriticalSection(&mutex->mutex);
195     SILC_ASSERT(mutex->locked == FALSE);
196     mutex->locked = TRUE;
197   }
198 #endif /* SILC_THREADS */
199 }
200
201 void silc_mutex_unlock(SilcMutex mutex)
202 {
203 #ifdef SILC_THREADS
204   if (mutex) {
205     SILC_ASSERT(mutex->locked == TRUE);
206     mutex->locked = FALSE;
207     LeaveCriticalSection(&mutex->mutex);
208   }
209 #endif /* SILC_THREADS */
210 }
211
212 void silc_mutex_assert_locked(SilcMutex mutex)
213 {
214 #ifdef SILC_THREADS
215   if (mutex)
216     SILC_ASSERT(mutex->locked);
217 #endif /* SILC_THREADS */
218 }
219
220
221 /***************************** SILC Rwlock API ******************************/
222
223 /* SILC read/write lock structure */
224 struct SilcRwLockStruct {
225 #ifdef SILC_THREADS
226   SilcMutex mutex;
227   SilcCond cond;
228 #endif /* SILC_THREADS */
229   unsigned int readers : 31;
230   unsigned int locked  : 1;
231 };
232
233 SilcBool silc_rwlock_alloc(SilcRwLock *rwlock)
234 {
235 #ifdef SILC_THREADS
236   *rwlock = silc_calloc(1, sizeof(**rwlock));
237   if (!(*rwlock))
238     return FALSE;
239   if (!silc_mutex_alloc(&(*rwlock)->mutex)) {
240     silc_free(*rwlock);
241     return FALSE;
242   }
243   if (!silc_cond_alloc(&(*rwlock)->cond)) {
244     silc_mutex_free((*rwlock)->mutex);
245     silc_free(*rwlock);
246     return FALSE;
247   }
248   return TRUE;
249 #else
250   return FALSE;
251 #endif /* SILC_THREADS */
252 }
253
254 void silc_rwlock_free(SilcRwLock rwlock)
255 {
256 #ifdef SILC_THREADS
257   if (rwlock) {
258     silc_mutex_free(rwlock->mutex);
259     silc_cond_free(rwlock->cond);
260     silc_free(rwlock);
261   }
262 #endif /* SILC_THREADS */
263 }
264
265 void silc_rwlock_rdlock(SilcRwLock rwlock)
266 {
267 #ifdef SILC_THREADS
268   if (rwlock) {
269     silc_mutex_lock(rwlock->mutex);
270     rwlock->readers++;
271     silc_mutex_unlock(rwlock->mutex);
272   }
273 #endif /* SILC_THREADS */
274 }
275
276 void silc_rwlock_wrlock(SilcRwLock rwlock)
277 {
278 #ifdef SILC_THREADS
279   if (rwlock) {
280     silc_mutex_lock(rwlock->mutex);
281     while (rwlock->readers > 0)
282       silc_cond_wait(rwlock->cond, rwlock->mutex);
283     rwlock->locked = TRUE;
284   }
285 #endif /* SILC_THREADS */
286 }
287
288 void silc_rwlock_unlock(SilcRwLock rwlock)
289 {
290 #ifdef SILC_THREADS
291   if (rwlock) {
292     if (rwlock->locked) {
293       /* Unlock writer */
294       rwlock->locked = FALSE;
295       silc_mutex_unlock(rwlock->mutex);
296       return;
297     }
298
299     /* Unlock reader */
300     silc_mutex_lock(rwlock->mutex);
301     rwlock->readers--;
302     silc_cond_broadcast(rwlock->cond);
303     silc_mutex_unlock(rwlock->mutex);
304   }
305 #endif /* SILC_THREADS */
306 }
307
308
309 /**************************** SILC Cond API ******************************/
310
311 /* SILC Conditional Variable context */
312 struct SilcCondStruct {
313 #ifdef SILC_THREADS
314   HANDLE event;
315 #endif /* SILC_THREADS*/
316   unsigned int waiters : 23;
317   unsigned int signal  : 1;
318 };
319
320 SilcBool silc_cond_alloc(SilcCond *cond)
321 {
322 #ifdef SILC_THREADS
323   *cond = silc_calloc(1, sizeof(**cond));
324   if (*cond == NULL)
325     return FALSE;
326   (*cond)->event = CreateEvent(NULL, TRUE, FALSE, NULL);
327   return TRUE;
328 #else
329   return FALSE;
330 #endif /* SILC_THREADS*/
331 }
332
333 void silc_cond_free(SilcCond cond)
334 {
335 #ifdef SILC_THREADS
336   CloseHandle(cond->event);
337   silc_free(cond);
338 #endif /* SILC_THREADS*/
339 }
340
341 void silc_cond_signal(SilcCond cond)
342 {
343 #ifdef SILC_THREADS
344   cond->signal = TRUE;
345   SetEvent(cond->event);
346 #endif /* SILC_THREADS*/
347 }
348
349 void silc_cond_broadcast(SilcCond cond)
350 {
351 #ifdef SILC_THREADS
352   cond->signal = TRUE;
353   SetEvent(cond->event);
354 #endif /* SILC_THREADS*/
355 }
356
357 void silc_cond_wait(SilcCond cond, SilcMutex mutex)
358 {
359 #ifdef SILC_THREADS
360   silc_cond_timedwait(cond, mutex, 0);
361 #endif /* SILC_THREADS*/
362 }
363
364 SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex,
365                              int timeout)
366 {
367 #ifdef SILC_THREADS
368   DWORD ret, t = INFINITE;
369
370   if (timeout)
371     t = timeout;
372
373   while (TRUE) {
374     cond->waiters++;
375     silc_mutex_unlock(mutex);
376
377     ret = WaitForSingleObject(cond->event, t);
378
379     silc_mutex_lock(mutex);
380     cond->waiters--;
381
382     if (ret != WAIT_OBJECT_0)
383       return FALSE;
384
385     if (cond->signal) {
386       cond->signal = FALSE;
387       ResetEvent(cond->event);
388       break;
389     }
390   }
391 #endif /* SILC_THREADS*/
392   return TRUE;
393 }