Added silc_mutex_trylock
[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 SilcBool silc_mutex_trylock(SilcMutex mutex)
204 {
205 #ifdef SILC_THREADS
206   if (mutex) {
207     if (pthread_mutex_trylock(&mutex->mutex) == 0) {
208       mutex->locked = TRUE;
209       return TRUE;
210     }
211   }
212 #endif /* SILC_THREADS */
213   return FALSE;
214 }
215
216 void silc_mutex_assert_locked(SilcMutex mutex)
217 {
218 #ifdef SILC_THREADS
219   if (mutex)
220     SILC_VERIFY(mutex->locked);
221 #endif /* SILC_THREADS */
222 }
223
224 /***************************** SILC Rwlock API ******************************/
225
226 /* SILC read/write lock structure */
227 struct SilcRwLockStruct {
228 #ifdef SILC_THREADS
229   pthread_rwlock_t rwlock;
230 #else
231   void *tmp;
232 #endif /* SILC_THREADS */
233 };
234
235 SilcBool silc_rwlock_alloc(SilcRwLock *rwlock)
236 {
237 #ifdef SILC_THREADS
238   *rwlock = silc_calloc(1, sizeof(**rwlock));
239   if (*rwlock == NULL)
240     return FALSE;
241   if (pthread_rwlock_init(&(*rwlock)->rwlock, NULL)) {
242     silc_set_errno_posix(errno);
243     silc_free(*rwlock);
244     return FALSE;
245   }
246   return TRUE;
247 #else
248   return FALSE;
249 #endif /* SILC_THREADS */
250 }
251
252 void silc_rwlock_free(SilcRwLock rwlock)
253 {
254 #ifdef SILC_THREADS
255   if (rwlock) {
256     pthread_rwlock_destroy(&rwlock->rwlock);
257     silc_free(rwlock);
258   }
259 #endif /* SILC_THREADS */
260 }
261
262 void silc_rwlock_rdlock(SilcRwLock rwlock)
263 {
264 #ifdef SILC_THREADS
265   if (rwlock)
266     pthread_rwlock_rdlock(&rwlock->rwlock);
267 #endif /* SILC_THREADS */
268 }
269
270 void silc_rwlock_wrlock(SilcRwLock rwlock)
271 {
272 #ifdef SILC_THREADS
273   if (rwlock)
274     SILC_VERIFY(pthread_rwlock_wrlock(&rwlock->rwlock) == 0);
275 #endif /* SILC_THREADS */
276 }
277
278 void silc_rwlock_unlock(SilcRwLock rwlock)
279 {
280 #ifdef SILC_THREADS
281   if (rwlock)
282     SILC_VERIFY(pthread_rwlock_unlock(&rwlock->rwlock) == 0);
283 #endif /* SILC_THREADS */
284 }
285
286 /****************************** SILC Cond API *******************************/
287
288 /* SILC Conditional Variable context */
289 struct SilcCondStruct {
290 #ifdef SILC_THREADS
291   pthread_cond_t cond;
292 #else
293   void *tmp;
294 #endif /* SILC_THREADS*/
295 };
296
297 SilcBool silc_cond_alloc(SilcCond *cond)
298 {
299 #ifdef SILC_THREADS
300   *cond = silc_calloc(1, sizeof(**cond));
301   if (*cond == NULL)
302     return FALSE;
303   if (pthread_cond_init(&(*cond)->cond, NULL)) {
304     silc_set_errno_posix(errno);
305     silc_free(*cond);
306     return FALSE;
307   }
308   return TRUE;
309 #else
310   return FALSE;
311 #endif /* SILC_THREADS*/
312 }
313
314 void silc_cond_free(SilcCond cond)
315 {
316 #ifdef SILC_THREADS
317   pthread_cond_destroy(&cond->cond);
318   silc_free(cond);
319 #endif /* SILC_THREADS*/
320 }
321
322 void silc_cond_signal(SilcCond cond)
323 {
324 #ifdef SILC_THREADS
325   pthread_cond_signal(&cond->cond);
326 #endif /* SILC_THREADS*/
327 }
328
329 void silc_cond_broadcast(SilcCond cond)
330 {
331 #ifdef SILC_THREADS
332   pthread_cond_broadcast(&cond->cond);
333 #endif /* SILC_THREADS*/
334 }
335
336 void silc_cond_wait(SilcCond cond, SilcMutex mutex)
337 {
338 #ifdef SILC_THREADS
339   pthread_cond_wait(&cond->cond, &mutex->mutex);
340 #endif /* SILC_THREADS*/
341 }
342
343 SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex,
344                              int timeout)
345 {
346 #ifdef SILC_THREADS
347   struct timespec t;
348   if (timeout) {
349     t.tv_sec = timeout / 1000;
350     t.tv_nsec = (timeout % 1000) * 1000;
351     return pthread_cond_timedwait(&cond->cond, &mutex->mutex, &t) == 0;
352   }
353
354   return pthread_cond_wait(&cond->cond, &mutex->mutex) == 0;
355 #else
356   return FALSE;
357 #endif /* SILC_THREADS*/
358 }
359
360 /************************** Thread-local Storage ****************************/
361
362 #if (defined(SILC_THREADS) && defined(HAVE_PTHREAD_KEY_CREATE) && \
363      defined(HAVE_PTHREAD_ONCE))
364
365 static SilcBool key_set = FALSE;
366 static pthread_key_t key;
367 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
368
369 static void silc_thread_tls_destructor(void *context)
370 {
371   SilcTls tls = context;
372
373   if (tls->tls_variables)
374     silc_hash_table_free(tls->tls_variables);
375   tls->tls_variables = NULL;
376
377   silc_free(tls);
378 }
379
380 static void silc_thread_tls_alloc(void)
381 {
382   if (pthread_key_create(&key, silc_thread_tls_destructor))
383     SILC_LOG_ERROR(("Error creating Thread-local storage"));
384   key_set = TRUE;
385 }
386
387 SilcTls silc_thread_tls_init(void)
388 {
389   SilcTls tls;
390
391   pthread_once(&key_once, silc_thread_tls_alloc);
392
393   if (silc_thread_get_tls())
394     return silc_thread_get_tls();
395
396   /* Allocate Tls for the thread */
397   tls = silc_calloc(1, sizeof(*tls));
398   if (!tls) {
399     SILC_LOG_ERROR(("Error allocating Thread-local storage"));
400     return NULL;
401   }
402   pthread_setspecific(key, tls);
403
404   /* Allocate global lock */
405   silc_mutex_alloc(&tls->lock);
406
407   return tls;
408 }
409
410 static SilcTls silc_thread_tls_init_shared(SilcTls other)
411 {
412   SilcTls tls;
413
414   pthread_once(&key_once, silc_thread_tls_alloc);
415
416   if (silc_thread_get_tls())
417     return silc_thread_get_tls();
418
419   /* Allocate Tls for the thread */
420   tls = silc_calloc(1, sizeof(*tls));
421   if (!tls) {
422     SILC_LOG_ERROR(("Error allocating Thread-local storage"));
423     return NULL;
424   }
425   pthread_setspecific(key, tls);
426
427   /* Take shared data */
428   tls->shared_data = 1;
429   tls->lock = other->lock;
430   tls->variables = other->variables;
431
432   return tls;
433 }
434
435 SilcTls silc_thread_get_tls(void)
436 {
437   if (!key_set)
438     return NULL;
439   return pthread_getspecific(key);
440 }
441
442 void silc_thread_tls_uninit(void)
443 {
444   SilcTls tls = silc_thread_get_tls();
445
446   if (!tls || tls->shared_data)
447     return;
448
449   /* Main thread cleanup */
450   if (tls->tls_variables)
451     silc_hash_table_free(tls->tls_variables);
452   if (tls->variables)
453     silc_hash_table_free(tls->variables);
454   if (tls->lock)
455     silc_mutex_free(tls->lock);
456   tls->variables = NULL;
457   tls->lock = NULL;
458   silc_free(tls);
459   pthread_setspecific(key, NULL);
460 }
461
462 #else
463
464 SilcTlsStruct tls;
465 SilcTls tls_ptr = NULL;
466
467 SilcTls silc_thread_tls_init(void)
468 {
469   if (silc_thread_get_tls())
470     return silc_thread_get_tls();
471
472   tls_ptr = &tls;
473   memset(tls_ptr, 0, sizeof(*tls_ptr));
474
475   atexit(silc_thread_tls_uninit);
476
477   return tls_ptr;
478 }
479
480 static SilcTls silc_thread_tls_init_shared(SilcTls other)
481 {
482   return silc_thread_tls_init();
483 }
484
485 SilcTls silc_thread_get_tls(void)
486 {
487   return tls_ptr;
488 }
489
490 void silc_thread_tls_uninit(void)
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
500 #endif