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