Added SILC Rand API, SILC Global Variables API and silcruntime.h.in
[runtime.git] / lib / silcutil / symbian / silcsymbianthread.cpp
1 /*
2
3   silcsymbianthread.cpp
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2006 - 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 #include <e32base.h>
22 #include <e32std.h>
23
24 /************************ Static utility functions **************************/
25
26 static SilcTls silc_thread_tls_init_shared(SilcTls other);
27
28 /**************************** SILC Thread API *******************************/
29
30 extern "C" {
31
32 /* Thread structure for Symbian */
33 struct SilcSymbianThread {
34 #ifdef SILC_THREADS
35   SilcThreadStart start_func;
36   void *context;
37   SilcBool waitable;
38 #endif
39   SilcTls tls;
40 };
41
42 /* The actual thread function */
43
44 static TInt silc_thread_start(TAny *context)
45 {
46 #ifdef SILC_THREADS
47   SilcSymbianThread *tc = (SilcSymbianThread *)context;
48   SilcThreadStart start_func = tc->start_func;
49   void *user_context = tc->context;
50   SilcBool waitable = tc->waitable;
51   SilcTls tls, other = tc->tls;
52   void *ret = NULL;
53
54   silc_free(tc);
55
56   tls = silc_thread_tls_init_shared(other);
57
58   CTrapCleanup *cs = CTrapCleanup::New();
59   if (cs) {
60     CActiveScheduler *s = new CActiveScheduler;
61     if(s) {
62       CActiveScheduler::Install(s);
63
64       /* Call the thread function */
65       TRAPD(ret_val, ret = start_func(user_context));
66
67       delete s;
68     }
69     delete cs;
70   }
71
72   if (tls->tls_variables)
73     silc_hash_table_free(tls->tls_variables);
74   silc_free(tls);
75   silc_thread_exit(ret);
76
77 #endif
78   return KErrNone;
79 }
80
81 /* Executed new thread */
82
83 SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
84                               SilcBool waitable)
85 {
86 #ifdef SILC_THREADS
87   SilcSymbianThread *tc;
88   RThread *thread;
89   TInt ret;
90   char tmp[24];
91   SilcUInt16 wname[24];
92
93   SILC_LOG_DEBUG(("Creating new thread"));
94
95   tc = (SilcSymbianThread *)silc_calloc(1, sizeof(*tc));
96   if (!tc)
97     return NULL;
98   tc->start_func = start_func;
99   tc->context = context;
100   tc->waitable = waitable;
101
102   /* Allocate thread */
103   thread = new RThread;
104   if (!thread) {
105     silc_free(tc);
106     return NULL;
107   }
108
109   /* Create the thread */
110   silc_snprintf(tmp, sizeof(tmp), "thread-%p", tc);
111   silc_utf8_c2w((const unsigned char *)tmp, strlen(tmp), wname,
112                 sizeof(wname) / sizeof(wname[0]));
113   TBuf<24> name((unsigned short *)wname);
114   name.PtrZ();
115   ret = thread->Create(name, silc_thread_start, 8192, NULL, tc);
116   if (ret != KErrNone) {
117     SILC_LOG_ERROR(("Could not create new thread, error %d", ret));
118     delete thread;
119     silc_free(tc);
120     return NULL;
121   }
122
123   /* Start the thread */
124   thread->Resume();
125
126   /* Close our instance to the thread */
127   thread->Close();
128
129   return (SilcThread)thread;
130 #else
131   /* Call thread callback immediately */
132   (*start_func)(context);
133   return NULL;
134 #endif
135 }
136
137 /* Exits current thread */
138
139 void silc_thread_exit(void *exit_value)
140 {
141 #ifdef SILC_THREADS
142   RThread().Kill((TInt)exit_value);
143 #endif
144 }
145
146 /* Returns current thread context */
147
148 SilcThread silc_thread_self(void)
149 {
150 #ifdef SILC_THREADS
151   RThread thread = RThread();
152   return (SilcThread)&thread;
153 #else
154   return NULL;
155 #endif
156 }
157
158 /* Blocks calling thread to wait for `thread' to finish. */
159
160 SilcBool silc_thread_wait(SilcThread thread, void **exit_value)
161 {
162 #ifdef SILC_THREADS
163   TRequestStatus req;
164   RThread *t = (RThread *)thread;
165   t->Logon(req);
166   User::WaitForAnyRequest();
167   return TRUE;
168 #else
169   return FALSE;
170 #endif
171 }
172
173 /* Yield processor */
174
175 void silc_thread_yield(void)
176 {
177 #ifdef SILC_THREADS
178   User::After(1);
179 #endif /* SILC_THREADS */
180 }
181
182 /***************************** SILC Mutex API *******************************/
183
184 /* SILC Mutex structure */
185 struct SilcMutexStruct {
186 #ifdef SILC_THREADS
187   RMutex *mutex;
188 #endif /* SILC_THREADS */
189   unsigned int locked : 1;
190 };
191
192 SilcBool silc_mutex_alloc(SilcMutex *mutex)
193 {
194 #ifdef SILC_THREADS
195   *mutex = (SilcMutex)silc_calloc(1, sizeof(**mutex));
196   if (*mutex == NULL)
197     return FALSE;
198   (*mutex)->mutex = new RMutex();
199   if (!(*mutex)->mutex) {
200     silc_free(*mutex);
201     return FALSE;
202   }
203   if ((*mutex)->mutex->CreateLocal() != KErrNone) {
204     delete (*mutex)->mutex;
205     silc_free(*mutex);
206     return FALSE;
207   }
208   (*mutex)->locked = FALSE;
209   return TRUE;
210 #else
211   return FALSE;
212 #endif /* SILC_THREADS */
213 }
214
215 void silc_mutex_free(SilcMutex mutex)
216 {
217 #ifdef SILC_THREADS
218   if (mutex) {
219     mutex->mutex->Close();
220     delete mutex->mutex;
221     silc_free(mutex);
222   }
223 #endif /* SILC_THREADS */
224 }
225
226 void silc_mutex_lock(SilcMutex mutex)
227 {
228 #ifdef SILC_THREADS
229   if (mutex) {
230     mutex->mutex->Wait();
231     mutex->locked = TRUE;
232   }
233 #endif /* SILC_THREADS */
234 }
235
236 void silc_mutex_unlock(SilcMutex mutex)
237 {
238 #ifdef SILC_THREADS
239   if (mutex) {
240     mutex->locked = FALSE;
241     mutex->mutex->Signal();
242   }
243 #endif /* SILC_THREADS */
244 }
245
246 void silc_mutex_assert_locked(SilcMutex mutex)
247 {
248 #ifdef SILC_THREADS
249   if (mutex)
250     SILC_ASSERT(mutex->locked);
251 #endif /* SILC_THREADS */
252 }
253
254 /***************************** SILC Rwlock API *****************************/
255
256 /* SILC read/write lock structure */
257 struct SilcRwLockStruct {
258 #ifdef SILC_THREADS
259   SilcMutex mutex;
260   SilcCond cond;
261 #endif /* SILC_THREADS */
262   unsigned int readers : 31;
263   unsigned int locked  : 1;
264 };
265
266 SilcBool silc_rwlock_alloc(SilcRwLock *rwlock)
267 {
268 #ifdef SILC_THREADS
269   *rwlock = (SilcRwLock)silc_calloc(1, sizeof(**rwlock));
270   if (!(*rwlock))
271     return FALSE;
272   if (!silc_mutex_alloc(&(*rwlock)->mutex)) {
273     silc_free(*rwlock);
274     return FALSE;
275   }
276   if (!silc_cond_alloc(&(*rwlock)->cond)) {
277     silc_mutex_free((*rwlock)->mutex);
278     silc_free(*rwlock);
279     return FALSE;
280   }
281   return TRUE;
282 #else
283   return FALSE;
284 #endif /* SILC_THREADS */
285 }
286
287 void silc_rwlock_free(SilcRwLock rwlock)
288 {
289 #ifdef SILC_THREADS
290   if (rwlock) {
291     silc_mutex_free(rwlock->mutex);
292     silc_cond_free(rwlock->cond);
293     silc_free(rwlock);
294   }
295 #endif /* SILC_THREADS */
296 }
297
298 void silc_rwlock_rdlock(SilcRwLock rwlock)
299 {
300 #ifdef SILC_THREADS
301   if (rwlock) {
302     silc_mutex_lock(rwlock->mutex);
303     rwlock->readers++;
304     silc_mutex_unlock(rwlock->mutex);
305   }
306 #endif /* SILC_THREADS */
307 }
308
309 void silc_rwlock_wrlock(SilcRwLock rwlock)
310 {
311 #ifdef SILC_THREADS
312   if (rwlock) {
313     silc_mutex_lock(rwlock->mutex);
314     while (rwlock->readers > 0)
315       silc_cond_wait(rwlock->cond, rwlock->mutex);
316     rwlock->locked = TRUE;
317   }
318 #endif /* SILC_THREADS */
319 }
320
321 void silc_rwlock_unlock(SilcRwLock rwlock)
322 {
323 #ifdef SILC_THREADS
324   if (rwlock) {
325     if (rwlock->locked) {
326       /* Unlock writer */
327       rwlock->locked = FALSE;
328       silc_mutex_unlock(rwlock->mutex);
329       return;
330     }
331
332     /* Unlock reader */
333     silc_mutex_lock(rwlock->mutex);
334     rwlock->readers--;
335     silc_cond_broadcast(rwlock->cond);
336     silc_mutex_unlock(rwlock->mutex);
337   }
338 #endif /* SILC_THREADS */
339 }
340
341 /****************************** SILC Cond API *******************************/
342
343 /* SILC Conditional Variable context */
344 struct SilcCondStruct {
345 #ifdef SILC_THREADS
346   RCondVar *cond;
347 #else
348   void *tmp;
349 #endif /* SILC_THREADS*/
350 };
351
352 SilcBool silc_cond_alloc(SilcCond *cond)
353 {
354 #ifdef SILC_THREADS
355   *cond = (SilcCond)silc_calloc(1, sizeof(**cond));
356   if (*cond == NULL)
357     return FALSE;
358   (*cond)->cond = new RCondVar();
359   if (!(*cond)->cond) {
360     silc_free(*cond);
361     return FALSE;
362   }
363   if ((*cond)->cond->CreateLocal() != KErrNone) {
364     delete (*cond)->cond;
365     silc_free(*cond);
366     return FALSE;
367   }
368   return TRUE;
369 #else
370   return FALSE;
371 #endif /* SILC_THREADS*/
372 }
373
374 void silc_cond_free(SilcCond cond)
375 {
376 #ifdef SILC_THREADS
377   cond->cond->Close();
378   delete cond->cond;
379   silc_free(cond);
380 #endif /* SILC_THREADS*/
381 }
382
383 void silc_cond_signal(SilcCond cond)
384 {
385 #ifdef SILC_THREADS
386   cond->cond->Signal();
387 #endif /* SILC_THREADS*/
388 }
389
390 void silc_cond_broadcast(SilcCond cond)
391 {
392 #ifdef SILC_THREADS
393   cond->cond->Broadcast();
394 #endif /* SILC_THREADS*/
395 }
396
397 void silc_cond_wait(SilcCond cond, SilcMutex mutex)
398 {
399 #ifdef SILC_THREADS
400   cond->cond->Wait(*mutex->mutex);
401 #endif /* SILC_THREADS*/
402 }
403
404 SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex,
405                              int timeout)
406 {
407 #ifdef SILC_THREADS
408   TInt ret;
409   if (timeout) {
410     ret = cond->cond->TimedWait(*mutex->mutex, (TInt)timeout * 1000);
411     if (ret != KErrNone)
412       SILC_LOG_DEBUG(("TimedWait returned %d", ret));
413     return ret != KErrTimedOut;
414   }
415   return (cond->cond->Wait(*mutex->mutex) == KErrNone);
416 #else
417   return FALSE;
418 #endif /* SILC_THREADS*/
419 }
420
421 /************************** Thread-local Storage ****************************/
422
423 SilcTls silc_thread_tls_init(void)
424 {
425   SilcTls tls;
426
427   if (silc_thread_get_tls())
428     return silc_thread_get_tls();
429
430   /* Allocate Tls for the thread */
431   tls = (SilcTls)silc_calloc(1, sizeof(*tls));
432   if (!tls)
433     return NULL;
434
435   Dll::SetTls(tls);
436
437   /* Allocate global lock */
438   silc_mutex_alloc(&tls->lock);
439
440   return tls;
441 }
442
443 static SilcTls silc_thread_tls_init_shared(SilcTls other)
444 {
445   SilcTls tls;
446
447   if (silc_thread_get_tls())
448     return silc_thread_get_tls();
449
450   /* Allocate Tls for the thread */
451   tls = (SilcTls)silc_calloc(1, sizeof(*tls));
452   if (!tls)
453     return NULL;
454
455   Dll::SetTls(tls);
456
457   /* Take shared data */
458   tls->shared_data = 1;
459   tls->lock = other->lock;
460   tls->variables = other->variables;
461
462   return tls;
463 }
464
465 SilcTls silc_thread_get_tls(void)
466 {
467   return STATIC_CAST(SilcTls, Dll::Tls());
468 }
469
470 void silc_thread_tls_uninit(void)
471 {
472   SilcTls tls = silc_thread_get_tls();
473
474   if (!tls || tls->shared_data)
475     return;
476
477   if (tls->tls_variables)
478     silc_hash_table_free(tls->tls_variables);
479   if (tls->variables)
480     silc_hash_table_free(tls->variables);
481   if (tls->lock)
482     silc_mutex_free(tls->lock);
483
484   tls->variables = NULL;
485   tls->lock = NULL;
486 }
487
488 } /* extern "C" */