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