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