Added SILC Rand API, SILC Global Variables API and silcruntime.h.in
[runtime.git] / lib / silcutil / unix / silcunixthread.c
1 /*
2
3   silcunixthread.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 typedef struct {
29   SilcTls tls;
30   SilcThreadStart start_func;
31   void *context;
32 } *SilcThreadStartContext;
33
34 static void *silc_thread_start(void *context)
35 {
36   SilcThreadStartContext c = context;
37   SilcThreadStart start_func = c->start_func;
38   void *start_context = c->context;
39   SilcTls other = c->tls;
40
41   silc_free(c);
42
43   silc_thread_tls_init_shared(other);
44
45   return start_func(start_context);
46 }
47
48 SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
49                               SilcBool waitable)
50 {
51 #ifdef SILC_THREADS
52   SilcThreadStartContext c;
53   pthread_attr_t attr;
54   pthread_t thread;
55   int ret;
56
57   SILC_LOG_DEBUG(("Creating new thread"));
58
59   if (!start_func)
60     return NULL;
61
62   c = silc_calloc(1, sizeof(*c));
63   if (!c)
64     return NULL;
65   c->start_func = start_func;
66   c->context = context;
67   c->tls = silc_thread_get_tls();
68
69   if (pthread_attr_init(&attr)) {
70     silc_set_errno_posix(errno);
71     SILC_LOG_ERROR(("Thread error: %s", silc_errno_string(silc_errno)));
72     silc_free(c);
73     return NULL;
74   }
75
76   if (pthread_attr_setdetachstate(&attr,
77                                   waitable ? PTHREAD_CREATE_JOINABLE :
78                                   PTHREAD_CREATE_DETACHED)) {
79     silc_set_errno_posix(errno);
80     SILC_LOG_ERROR(("Thread error: %s", silc_errno_string(silc_errno)));
81     pthread_attr_destroy(&attr);
82     silc_free(c);
83     return NULL;
84   }
85
86   ret = pthread_create(&thread, &attr, silc_thread_start, c);
87   if (ret) {
88     silc_set_errno_posix(errno);
89     SILC_LOG_ERROR(("Thread error: %s", silc_errno_string(silc_errno)));
90     pthread_attr_destroy(&attr);
91     silc_free(c);
92     return NULL;
93   }
94
95   pthread_attr_destroy(&attr);
96
97   SILC_LOG_DEBUG(("Created thread %p", (SilcThread)thread));
98
99   return (SilcThread)thread;
100 #else
101   /* Call thread callback immediately */
102   (*start_func)(context);
103   return NULL;
104 #endif
105 }
106
107 void silc_thread_exit(void *exit_value)
108 {
109 #ifdef SILC_THREADS
110   pthread_exit(exit_value);
111 #endif
112 }
113
114 SilcThread silc_thread_self(void)
115 {
116 #ifdef SILC_THREADS
117   pthread_t self = pthread_self();
118   return (SilcThread)self;
119 #else
120   return NULL;
121 #endif
122 }
123
124 SilcBool silc_thread_wait(SilcThread thread, void **exit_value)
125 {
126 #ifdef SILC_THREADS
127   SILC_LOG_DEBUG(("Waiting for thread %p", thread));
128   if (!pthread_join(*(pthread_t *)thread, exit_value))
129     return TRUE;
130   return FALSE;
131 #else
132   return FALSE;
133 #endif
134 }
135
136 void silc_thread_yield(void)
137 {
138 #ifdef SILC_THREADS
139 #ifdef HAVE_SCHED_YIELD
140   sched_yield();
141 #endif /* HAVE_SCHED_YIELD */
142 #endif /* SILC_THREADS */
143 }
144
145 /***************************** SILC Mutex API *******************************/
146
147 /* SILC Mutex structure */
148 struct SilcMutexStruct {
149 #ifdef SILC_THREADS
150   pthread_mutex_t mutex;
151 #endif /* SILC_THREADS */
152   unsigned int locked : 1;
153 };
154
155 SilcBool silc_mutex_alloc(SilcMutex *mutex)
156 {
157 #ifdef SILC_THREADS
158   *mutex = silc_calloc(1, sizeof(**mutex));
159   if (*mutex == NULL)
160     return FALSE;
161   if (pthread_mutex_init(&(*mutex)->mutex, NULL)) {
162     silc_set_errno_posix(errno);
163     silc_free(*mutex);
164     return FALSE;
165   }
166   (*mutex)->locked = FALSE;
167   return TRUE;
168 #else
169   return FALSE;
170 #endif /* SILC_THREADS */
171 }
172
173 void silc_mutex_free(SilcMutex mutex)
174 {
175 #ifdef SILC_THREADS
176   if (mutex) {
177     pthread_mutex_destroy(&mutex->mutex);
178     silc_free(mutex);
179   }
180 #endif /* SILC_THREADS */
181 }
182
183 void silc_mutex_lock(SilcMutex mutex)
184 {
185 #ifdef SILC_THREADS
186   if (mutex) {
187     SILC_VERIFY(pthread_mutex_lock(&mutex->mutex) == 0);
188     mutex->locked = TRUE;
189   }
190 #endif /* SILC_THREADS */
191 }
192
193 void silc_mutex_unlock(SilcMutex mutex)
194 {
195 #ifdef SILC_THREADS
196   if (mutex) {
197     mutex->locked = FALSE;
198     SILC_VERIFY(pthread_mutex_unlock(&mutex->mutex) == 0);
199   }
200 #endif /* SILC_THREADS */
201 }
202
203 void silc_mutex_assert_locked(SilcMutex mutex)
204 {
205 #ifdef SILC_THREADS
206   if (mutex)
207     SILC_VERIFY(mutex->locked);
208 #endif /* SILC_THREADS */
209 }
210
211 /***************************** SILC Rwlock API ******************************/
212
213 /* SILC read/write lock structure */
214 struct SilcRwLockStruct {
215 #ifdef SILC_THREADS
216   pthread_rwlock_t rwlock;
217 #else
218   void *tmp;
219 #endif /* SILC_THREADS */
220 };
221
222 SilcBool silc_rwlock_alloc(SilcRwLock *rwlock)
223 {
224 #ifdef SILC_THREADS
225   *rwlock = silc_calloc(1, sizeof(**rwlock));
226   if (*rwlock == NULL)
227     return FALSE;
228   if (pthread_rwlock_init(&(*rwlock)->rwlock, NULL)) {
229     silc_set_errno_posix(errno);
230     silc_free(*rwlock);
231     return FALSE;
232   }
233   return TRUE;
234 #else
235   return FALSE;
236 #endif /* SILC_THREADS */
237 }
238
239 void silc_rwlock_free(SilcRwLock rwlock)
240 {
241 #ifdef SILC_THREADS
242   if (rwlock) {
243     pthread_rwlock_destroy(&rwlock->rwlock);
244     silc_free(rwlock);
245   }
246 #endif /* SILC_THREADS */
247 }
248
249 void silc_rwlock_rdlock(SilcRwLock rwlock)
250 {
251 #ifdef SILC_THREADS
252   if (rwlock)
253     pthread_rwlock_rdlock(&rwlock->rwlock);
254 #endif /* SILC_THREADS */
255 }
256
257 void silc_rwlock_wrlock(SilcRwLock rwlock)
258 {
259 #ifdef SILC_THREADS
260   if (rwlock)
261     SILC_VERIFY(pthread_rwlock_wrlock(&rwlock->rwlock) == 0);
262 #endif /* SILC_THREADS */
263 }
264
265 void silc_rwlock_unlock(SilcRwLock rwlock)
266 {
267 #ifdef SILC_THREADS
268   if (rwlock)
269     SILC_VERIFY(pthread_rwlock_unlock(&rwlock->rwlock) == 0);
270 #endif /* SILC_THREADS */
271 }
272
273 /****************************** SILC Cond API *******************************/
274
275 /* SILC Conditional Variable context */
276 struct SilcCondStruct {
277 #ifdef SILC_THREADS
278   pthread_cond_t cond;
279 #else
280   void *tmp;
281 #endif /* SILC_THREADS*/
282 };
283
284 SilcBool silc_cond_alloc(SilcCond *cond)
285 {
286 #ifdef SILC_THREADS
287   *cond = silc_calloc(1, sizeof(**cond));
288   if (*cond == NULL)
289     return FALSE;
290   if (pthread_cond_init(&(*cond)->cond, NULL)) {
291     silc_set_errno_posix(errno);
292     silc_free(*cond);
293     return FALSE;
294   }
295   return TRUE;
296 #else
297   return FALSE;
298 #endif /* SILC_THREADS*/
299 }
300
301 void silc_cond_free(SilcCond cond)
302 {
303 #ifdef SILC_THREADS
304   pthread_cond_destroy(&cond->cond);
305   silc_free(cond);
306 #endif /* SILC_THREADS*/
307 }
308
309 void silc_cond_signal(SilcCond cond)
310 {
311 #ifdef SILC_THREADS
312   pthread_cond_signal(&cond->cond);
313 #endif /* SILC_THREADS*/
314 }
315
316 void silc_cond_broadcast(SilcCond cond)
317 {
318 #ifdef SILC_THREADS
319   pthread_cond_broadcast(&cond->cond);
320 #endif /* SILC_THREADS*/
321 }
322
323 void silc_cond_wait(SilcCond cond, SilcMutex mutex)
324 {
325 #ifdef SILC_THREADS
326   pthread_cond_wait(&cond->cond, &mutex->mutex);
327 #endif /* SILC_THREADS*/
328 }
329
330 SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex,
331                              int timeout)
332 {
333 #ifdef SILC_THREADS
334   struct timespec t;
335   if (timeout) {
336     t.tv_sec = timeout / 1000;
337     t.tv_nsec = (timeout % 1000) * 1000;
338     return pthread_cond_timedwait(&cond->cond, &mutex->mutex, &t) == 0;
339   }
340
341   return pthread_cond_wait(&cond->cond, &mutex->mutex) == 0;
342 #else
343   return FALSE;
344 #endif /* SILC_THREADS*/
345 }
346
347 /************************** Thread-local Storage ****************************/
348
349 #if (defined(SILC_THREADS) && defined(HAVE_PTHREAD_KEY_CREATE) && \
350      defined(HAVE_PTHREAD_ONCE))
351
352 static SilcBool key_set = FALSE;
353 static pthread_key_t key;
354 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
355
356 static void silc_thread_tls_destructor(void *context)
357 {
358   SilcTls tls = context;
359
360   if (tls->tls_variables)
361     silc_hash_table_free(tls->tls_variables);
362   tls->tls_variables = NULL;
363
364   silc_free(tls);
365 }
366
367 static void silc_thread_tls_alloc(void)
368 {
369   if (pthread_key_create(&key, silc_thread_tls_destructor))
370     SILC_LOG_ERROR(("Error creating Thread-local storage"));
371   key_set = TRUE;
372 }
373
374 SilcTls silc_thread_tls_init(void)
375 {
376   SilcTls tls;
377
378   pthread_once(&key_once, silc_thread_tls_alloc);
379
380   if (silc_thread_get_tls())
381     return silc_thread_get_tls();
382
383   /* Allocate Tls for the thread */
384   tls = silc_calloc(1, sizeof(*tls));
385   if (!tls) {
386     SILC_LOG_ERROR(("Error allocating Thread-local storage"));
387     return NULL;
388   }
389   pthread_setspecific(key, tls);
390
391   /* Allocate global lock */
392   silc_mutex_alloc(&tls->lock);
393
394   return tls;
395 }
396
397 static SilcTls silc_thread_tls_init_shared(SilcTls other)
398 {
399   SilcTls tls;
400
401   pthread_once(&key_once, silc_thread_tls_alloc);
402
403   if (silc_thread_get_tls())
404     return silc_thread_get_tls();
405
406   /* Allocate Tls for the thread */
407   tls = silc_calloc(1, sizeof(*tls));
408   if (!tls) {
409     SILC_LOG_ERROR(("Error allocating Thread-local storage"));
410     return NULL;
411   }
412   pthread_setspecific(key, tls);
413
414   /* Take shared data */
415   tls->shared_data = 1;
416   tls->lock = other->lock;
417   tls->variables = other->variables;
418
419   return tls;
420 }
421
422 SilcTls silc_thread_get_tls(void)
423 {
424   if (!key_set)
425     return NULL;
426   return pthread_getspecific(key);
427 }
428
429 void silc_thread_tls_uninit(void)
430 {
431   SilcTls tls = silc_thread_get_tls();
432
433   if (!tls || tls->shared_data)
434     return;
435
436   /* Main thread cleanup */
437   if (tls->tls_variables)
438     silc_hash_table_free(tls->tls_variables);
439   if (tls->variables)
440     silc_hash_table_free(tls->variables);
441   if (tls->lock)
442     silc_mutex_free(tls->lock);
443   tls->variables = NULL;
444   tls->lock = NULL;
445   silc_free(tls);
446   pthread_setspecific(key, NULL);
447 }
448
449 #else
450
451 SilcTlsStruct tls;
452 SilcTls tls_ptr = NULL;
453
454 SilcTls silc_thread_tls_init(void)
455 {
456   if (silc_thread_get_tls())
457     return silc_thread_get_tls();
458
459   tls_ptr = &tls;
460   memset(tls_ptr, 0, sizeof(*tls_ptr));
461
462   atexit(silc_thread_tls_uninit);
463
464   return tls_ptr;
465 }
466
467 static SilcTls silc_thread_tls_init_shared(SilcTls other)
468 {
469   return silc_thread_tls_init();
470 }
471
472 SilcTls silc_thread_get_tls(void)
473 {
474   return tls_ptr;
475 }
476
477 void silc_thread_tls_uninit(void)
478 {
479   if (tls.tls_variables)
480     silc_hash_table_free(tls.tls_variables);
481   if (tls.variables)
482     silc_hash_table_free(tls.variables);
483   if (tls.lock)
484     silc_mutex_free(tls.lock);
485 }
486
487 #endif