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