Added SILC Thread Queue API
[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 /* Actual routine that is called by WIN32 when the thread is created.
36    We will call the start_func from here. When this returns the thread
37    is destroyed. */
38
39 unsigned __stdcall silc_thread_win32_start(void *context)
40 {
41   SilcWin32Thread thread = (SilcWin32Thread)context;
42   SilcTls tls;
43
44   tls = silc_thread_tls_init();
45   if (tls)
46     tls->platform_context = thread;
47
48   silc_thread_exit(thread->start_func(thread->context));
49
50   silc_free(tls);
51
52   return 0;
53 }
54 #endif
55
56 SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
57                               SilcBool waitable)
58 {
59 #ifdef SILC_THREADS
60   SilcWin32Thread thread;
61   unsigned id;
62
63   SILC_LOG_DEBUG(("Creating new thread"));
64
65   thread = silc_calloc(1, sizeof(*thread));
66   if (!thread)
67     return NULL;
68   thread->start_func = start_func;
69   thread->context = context;
70   thread->waitable = waitable;
71   thread->thread =
72     _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE)silc_thread_win32_start,
73                    (void *)thread, 0, &id);
74
75   if (!thread->thread) {
76     SILC_LOG_ERROR(("Could not create new thread"));
77     silc_set_errno_reason(SILC_ERR, "Could not create new thread");
78     silc_free(thread);
79     return NULL;
80   }
81
82   return (SilcThread)thread;
83 #else
84   /* Call thread callback immediately */
85   (*start_func)(context);
86   return NULL;
87 #endif
88 }
89
90 void silc_thread_exit(void *exit_value)
91 {
92 #ifdef SILC_THREADS
93   SilcTls tls = silc_thread_get_tls();
94   SilcWin32Thread thread = tls->platform_context;
95
96   if (thread) {
97     /* If the thread is waitable the memory is freed only in silc_thread_wait
98        by another thread. If not waitable, free it now. */
99     if (!thread->waitable)
100       silc_free(thread);
101   }
102
103   _endthreadex(0);
104 #endif
105 }
106
107 SilcThread silc_thread_self(void)
108 {
109 #ifdef SILC_THREADS
110   SilcTls tls = silc_thread_get_tls();
111   SilcWin32Thread self = tls->platform_context;
112
113   if (!self) {
114     /* This should only happen for the main thread. */
115     HANDLE handle = GetCurrentThread ();
116     HANDLE process = GetCurrentProcess ();
117     self = silc_calloc(1, sizeof(*self));
118     if (self) {
119       DuplicateHandle(process, handle, process,
120                       &self->thread, 0, FALSE,
121                       DUPLICATE_SAME_ACCESS);
122       tls->platform_context = self;
123     }
124   }
125
126   return (SilcThread)self;
127 #else
128   return NULL;
129 #endif
130 }
131
132 SilcBool silc_thread_wait(SilcThread thread, void **exit_value)
133 {
134 #ifdef SILC_THREADS
135   SilcWin32Thread self = (SilcWin32Thread)thread;
136
137   SILC_LOG_DEBUG(("Waiting for thread %p", self));
138
139   if (!self->waitable)
140     return FALSE;
141
142   /* The thread is waitable thus we will free all memory after the
143      WaitForSingleObject returns, the thread is destroyed after that. */
144   WaitForSingleObject(self->thread, INFINITE);
145   CloseHandle(self->thread);
146
147   if (exit_value)
148     *exit_value = NULL;
149
150   return TRUE;
151 #else
152   return FALSE;
153 #endif
154 }
155
156 void silc_thread_yield(void)
157 {
158 #ifdef SILC_THREADS
159   SleepEx (0,0);
160 #endif /* SILC_THREADS */
161 }
162
163
164 /***************************** SILC Mutex API *******************************/
165
166 /* SILC Mutex structure */
167 struct SilcMutexStruct {
168 #ifdef SILC_THREADS
169   CRITICAL_SECTION mutex;
170 #endif /* SILC_THREADS */
171   unsigned int locked : 1;
172 };
173
174 SilcBool silc_mutex_alloc(SilcMutex *mutex)
175 {
176 #ifdef SILC_THREADS
177   *mutex = silc_calloc(1, sizeof(**mutex));
178   if (!(*mutex))
179     return FALSE;
180   InitializeCriticalSection(&((*mutex)->mutex));
181   return TRUE;
182 #else
183   return FALSE;
184 #endif /* SILC_THREADS */
185 }
186
187 void silc_mutex_free(SilcMutex mutex)
188 {
189 #ifdef SILC_THREADS
190   if (mutex) {
191     DeleteCriticalSection(&mutex->mutex);
192     silc_free(mutex);
193   }
194 #endif /* SILC_THREADS */
195 }
196
197 void silc_mutex_lock(SilcMutex mutex)
198 {
199 #ifdef SILC_THREADS
200   if (mutex) {
201     EnterCriticalSection(&mutex->mutex);
202     SILC_ASSERT(mutex->locked == FALSE);
203     mutex->locked = TRUE;
204   }
205 #endif /* SILC_THREADS */
206 }
207
208 void silc_mutex_unlock(SilcMutex mutex)
209 {
210 #ifdef SILC_THREADS
211   if (mutex) {
212     SILC_ASSERT(mutex->locked == TRUE);
213     mutex->locked = FALSE;
214     LeaveCriticalSection(&mutex->mutex);
215   }
216 #endif /* SILC_THREADS */
217 }
218
219 void silc_mutex_assert_locked(SilcMutex mutex)
220 {
221 #ifdef SILC_THREADS
222   if (mutex)
223     SILC_ASSERT(mutex->locked);
224 #endif /* SILC_THREADS */
225 }
226
227
228 /***************************** SILC Rwlock API ******************************/
229
230 /* SILC read/write lock structure */
231 struct SilcRwLockStruct {
232 #ifdef SILC_THREADS
233   SilcMutex mutex;
234   SilcCond cond;
235 #endif /* SILC_THREADS */
236   unsigned int readers : 31;
237   unsigned int locked  : 1;
238 };
239
240 SilcBool silc_rwlock_alloc(SilcRwLock *rwlock)
241 {
242 #ifdef SILC_THREADS
243   *rwlock = silc_calloc(1, sizeof(**rwlock));
244   if (!(*rwlock))
245     return FALSE;
246   if (!silc_mutex_alloc(&(*rwlock)->mutex)) {
247     silc_free(*rwlock);
248     return FALSE;
249   }
250   if (!silc_cond_alloc(&(*rwlock)->cond)) {
251     silc_mutex_free((*rwlock)->mutex);
252     silc_free(*rwlock);
253     return FALSE;
254   }
255   return TRUE;
256 #else
257   return FALSE;
258 #endif /* SILC_THREADS */
259 }
260
261 void silc_rwlock_free(SilcRwLock rwlock)
262 {
263 #ifdef SILC_THREADS
264   if (rwlock) {
265     silc_mutex_free(rwlock->mutex);
266     silc_cond_free(rwlock->cond);
267     silc_free(rwlock);
268   }
269 #endif /* SILC_THREADS */
270 }
271
272 void silc_rwlock_rdlock(SilcRwLock rwlock)
273 {
274 #ifdef SILC_THREADS
275   if (rwlock) {
276     silc_mutex_lock(rwlock->mutex);
277     rwlock->readers++;
278     silc_mutex_unlock(rwlock->mutex);
279   }
280 #endif /* SILC_THREADS */
281 }
282
283 void silc_rwlock_wrlock(SilcRwLock rwlock)
284 {
285 #ifdef SILC_THREADS
286   if (rwlock) {
287     silc_mutex_lock(rwlock->mutex);
288     while (rwlock->readers > 0)
289       silc_cond_wait(rwlock->cond, rwlock->mutex);
290     rwlock->locked = TRUE;
291   }
292 #endif /* SILC_THREADS */
293 }
294
295 void silc_rwlock_unlock(SilcRwLock rwlock)
296 {
297 #ifdef SILC_THREADS
298   if (rwlock) {
299     if (rwlock->locked) {
300       /* Unlock writer */
301       rwlock->locked = FALSE;
302       silc_mutex_unlock(rwlock->mutex);
303       return;
304     }
305
306     /* Unlock reader */
307     silc_mutex_lock(rwlock->mutex);
308     rwlock->readers--;
309     silc_cond_broadcast(rwlock->cond);
310     silc_mutex_unlock(rwlock->mutex);
311   }
312 #endif /* SILC_THREADS */
313 }
314
315
316 /**************************** SILC Cond API ******************************/
317
318 /* SILC Conditional Variable context */
319 struct SilcCondStruct {
320 #ifdef SILC_THREADS
321   HANDLE event;
322 #endif /* SILC_THREADS*/
323   unsigned int waiters : 23;
324   unsigned int signal  : 1;
325 };
326
327 SilcBool silc_cond_alloc(SilcCond *cond)
328 {
329 #ifdef SILC_THREADS
330   *cond = silc_calloc(1, sizeof(**cond));
331   if (*cond == NULL)
332     return FALSE;
333   (*cond)->event = CreateEvent(NULL, TRUE, FALSE, NULL);
334   return TRUE;
335 #else
336   return FALSE;
337 #endif /* SILC_THREADS*/
338 }
339
340 void silc_cond_free(SilcCond cond)
341 {
342 #ifdef SILC_THREADS
343   CloseHandle(cond->event);
344   silc_free(cond);
345 #endif /* SILC_THREADS*/
346 }
347
348 void silc_cond_signal(SilcCond cond)
349 {
350 #ifdef SILC_THREADS
351   cond->signal = TRUE;
352   SetEvent(cond->event);
353 #endif /* SILC_THREADS*/
354 }
355
356 void silc_cond_broadcast(SilcCond cond)
357 {
358 #ifdef SILC_THREADS
359   cond->signal = TRUE;
360   SetEvent(cond->event);
361 #endif /* SILC_THREADS*/
362 }
363
364 void silc_cond_wait(SilcCond cond, SilcMutex mutex)
365 {
366 #ifdef SILC_THREADS
367   silc_cond_timedwait(cond, mutex, 0);
368 #endif /* SILC_THREADS*/
369 }
370
371 SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex,
372                              int timeout)
373 {
374 #ifdef SILC_THREADS
375   DWORD ret, t = INFINITE;
376
377   if (timeout)
378     t = timeout;
379
380   while (TRUE) {
381     cond->waiters++;
382     silc_mutex_unlock(mutex);
383
384     ret = WaitForSingleObject(cond->event, t);
385
386     silc_mutex_lock(mutex);
387     cond->waiters--;
388
389     if (ret != WAIT_OBJECT_0)
390       return FALSE;
391
392     if (cond->signal) {
393       cond->signal = FALSE;
394       ResetEvent(cond->event);
395       break;
396     }
397   }
398 #endif /* SILC_THREADS*/
399   return TRUE;
400 }
401
402 /************************** Thread-local Storage ****************************/
403
404 #ifdef SILC_THREADS
405
406 static DWORD silc_tls;
407 SilcBool silc_tls_set = FALSE;
408
409 SilcTls silc_thread_tls_init(void)
410 {
411   SilcTls tls;
412
413   if (!silc_tls_set) {
414     silc_tls = TlsAlloc();
415     if (silc_tls == TLS_OUT_OF_INDEXES) {
416       SILC_LOG_ERROR(("Error creating Thread-local storage"));
417       return NULL;
418     }
419
420     silc_tls_set = TRUE;
421   }
422
423   if (silc_thread_get_tls())
424     return silc_thread_get_tls();
425
426   /* Allocate Tls for the thread */
427   tls = silc_calloc(1, sizeof(*tls));
428   if (!tls) {
429     SILC_LOG_ERROR(("Error allocating Thread-local storage"));
430     return NULL;
431   }
432
433   TlsSetValue(silc_tls, tls);
434   return tls;
435 }
436
437 SilcTls silc_thread_get_tls(void)
438 {
439   return (SilcTls)TlsGetValue(silc_tls);
440 }
441
442 #else
443
444 SilcTlsStruct tls;
445 SilcTls tls_ptr = NULL;
446
447 SilcTls silc_thread_tls_init(void)
448 {
449   if (silc_thread_get_tls())
450     return silc_thread_get_tls();
451
452   tls_ptr = &tls;
453   memset(tls_ptr, 0, sizeof(*tls_ptr));
454   return tls_ptr;
455 }
456
457 SilcTls silc_thread_get_tls(void)
458 {
459   return tls_ptr;
460 }
461
462 #endif /* SILC_THREADS */