Added SILC Thread Queue API
[silc.git] / lib / silcutil / symbian / silcsymbianscheduler.cpp
1 /*
2
3   silcsymbianscheduler.cpp
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 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
23 /* The SILC Scheduler for Symbian handles only timeout tasks.  Fd tasks are
24    not handled by the SILC Scheduler at all but are handled by the Symbian's
25    Active Scheduler.  Fd and socket stream callbacks are delivered back
26    to caller in their respective class implementatios.
27
28    The SILC Scheduler in Symbian works by creating CActiveSchedulerWait
29    when silc_schedule() is called.  This will block the calling thread just
30    like silc_schedule is supposed to do.  Under that Active Scheduler we
31    run our SilcSymbianScheduler timer which will handle the actual SILC
32    Scheduler by calling silc_schedule_one at correct times.  The timeout
33    values are selected by the SILC Scheduler itself when silc_schedule_one
34    is called.  After that call returns we go back to the Active Scheduler
35    to dispatch other active objects and to wait for next timeout.
36
37    Wakeup of the scheduler works by simply cancelling the outstanding timeout
38    and issuing a zero timeout to call the silc_schedule_one again.
39
40    If user directly calls silc_schedule_one it behaves same as on other
41    platforms. */
42
43 class SilcSymbianScheduler;
44 class SilcSymbianSchedulerWakeup;
45
46 typedef struct {
47   SilcSymbianScheduler *timer;
48   SilcSymbianSchedulerWakeup *wakeup;
49 } *SilcSymbianInternal;
50
51 /* SILC scheduler timer class.  This handles the actual SILC Scheduler
52    by calling silc_schedule_one and scheduling the scheduler timeouts. */
53 class SilcSymbianScheduler : public CTimer {
54 public:
55   /* Constructor */
56   SilcSymbianScheduler() : CTimer(CActive::EPriorityStandard)
57   {
58     CTimer::ConstructL();
59     CActiveScheduler::Add(this);
60     After(0);
61   }
62
63   /* Destructor */
64   ~SilcSymbianScheduler()
65   {
66     Cancel();
67   }
68
69   /* Timeout callback */
70   virtual void RunL()
71   {
72     if (!silc_schedule_one(schedule, -1))
73       s->AsyncStop();
74   }
75
76   CActiveSchedulerWait *s;
77   SilcSchedule schedule;
78 };
79
80 /* Scheduler wakeup class */
81 class SilcSymbianSchedulerWakeup : public CActive {
82 public:
83   /* Constructor */
84   SilcSymbianSchedulerWakeup() : CActive(CActive::EPriorityStandard)
85   {
86     CActiveScheduler::Add(this);
87     iStatus = KRequestPending;
88     SetActive();
89   }
90
91   /* Destructor */
92   ~SilcSymbianSchedulerWakeup()
93   {
94     Cancel();
95   }
96
97   /* Wakeup.  This is called with scheduler locked. */
98   void Wakeup(TThreadId thread_id)
99   {
100     if (wake_signal)
101       return;
102     wake_signal = TRUE;
103
104     TRequestStatus *status = &iStatus;
105     if (id != thread_id)
106       thread.RequestComplete(status, KErrNone);
107     else
108       User::RequestComplete(status, KErrNone);
109   }
110
111   /* Timeout callback */
112   virtual void RunL()
113   {
114     SILC_LOG_DEBUG(("Wakeup scheduler"));
115
116     /* We need to synchronize with calls to Wakeup() */
117     silc_mutex_lock(schedule->lock);
118
119     /* Wakeup scheduler */
120     timer->Cancel();
121     timer->After(0);
122     wake_signal = FALSE;
123
124     iStatus = KRequestPending;
125     SetActive();
126
127     silc_mutex_unlock(schedule->lock);
128   }
129
130   virtual void DoCancel()
131   {
132     wake_signal = TRUE;
133   }
134
135   RThread thread;
136   TThreadId id;
137   SilcSymbianScheduler *timer;
138   SilcSchedule schedule;
139   unsigned int wake_signal  : 1;
140 };
141
142 extern "C" {
143
144 /* Symbian's silc_schedule call.  We start Active Scheduler here and start
145    our SILC Scheduler.  The calling thread will block here. */
146
147 void silc_schedule(SilcSchedule schedule)
148 {
149   SilcSymbianInternal internal = (SilcSymbianInternal)schedule->internal;
150   CActiveSchedulerWait *s;
151
152   SILC_LOG_DEBUG(("Running scheduler"));
153
154   /* Create Active Scheduler */
155   s = new CActiveSchedulerWait;
156   SILC_ASSERT(s);
157
158   /* Start SILC Scheduler */
159   internal->timer = new SilcSymbianScheduler;
160   SILC_ASSERT(internal->timer);
161   internal->timer->schedule = schedule;
162   internal->timer->s = s;
163   internal->wakeup = new SilcSymbianSchedulerWakeup;
164   SILC_ASSERT(internal->wakeup);
165   internal->wakeup->id = RThread().Id();
166   internal->wakeup->thread.Open(internal->wakeup->id);
167   internal->wakeup->timer = internal->timer;
168   internal->wakeup->schedule = schedule;
169
170   /* Start Active Scheduler */
171   s->Start();
172
173   delete internal->wakeup;
174   delete internal->timer;
175   delete s;
176 }
177
178 int silc_poll(SilcSchedule schedule, void *context)
179 {
180   SilcSymbianInternal internal = (SilcSymbianInternal)context;
181   int timeout = -1;
182   TTime at_timeout;
183
184   /* When user is using silc_schedule_one we don't have our timer set,
185      so just return immediately. */
186   if (!internal->timer)
187     return 0;
188
189   /* Schedule next timeout */
190   if (schedule->has_timeout)
191     timeout = ((schedule->timeout.tv_sec * 1000) +
192                 (schedule->timeout.tv_usec / 1000));
193
194   if (!timeout)
195     return 0;
196
197   if (timeout == -1)
198     return -2;
199
200   /* Set the timeout value */
201   at_timeout.HomeTime();
202   while (timeout > 2100 * 1000) {
203     at_timeout += (TTimeIntervalMicroSeconds32)(2100 * 1000 * 1000);
204     timeout -= (2100 * 1000);
205   }
206   at_timeout += (TTimeIntervalMicroSeconds32)(timeout * 1000);
207
208   /* Schedule the timeout */
209   if (internal->timer->IsActive())
210     internal->timer->Cancel();
211   internal->timer->At(at_timeout);
212
213   /* Return special "ignore" value.  Causes the scheduler to just break
214      the scheduler iteration and return back to its caller. */
215   return -2;
216 }
217
218 SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,
219                                             void *context,
220                                             SilcTaskFd task,
221                                             SilcTaskEvent event_mask)
222 {
223   /* Nothing to do */
224   return TRUE;
225 }
226
227 void *silc_schedule_internal_init(SilcSchedule schedule,
228                                   void *app_context)
229 {
230   SilcSymbianInternal internal;
231
232   internal = (SilcSymbianInternal)silc_calloc(1, sizeof(*internal));
233   if (!internal)
234     return NULL;
235
236   return internal;
237 }
238
239 void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
240 {
241   SilcSymbianInternal internal = (SilcSymbianInternal)context;
242   silc_free(internal);
243 }
244
245 void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
246 {
247 #ifdef SILC_THREADS
248   SilcSymbianInternal internal = (SilcSymbianInternal)context;
249   TThreadId id;
250
251   if (!internal->timer)
252     return;
253
254   id = RThread().Id();
255   internal->wakeup->Wakeup(id);
256 #endif /* SILC_THREADS */
257 }
258
259 void silc_schedule_internal_signal_register(SilcSchedule schedule,
260                                             void *context,
261                                             SilcUInt32 sig,
262                                             SilcTaskCallback callback,
263                                             void *callback_context)
264 {
265   /* Nothing to do */
266 }
267
268 void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
269                                               void *context,
270                                               SilcUInt32 sig)
271 {
272   /* Nothing to do */
273 }
274
275 void silc_schedule_internal_signals_call(SilcSchedule schedule, void *context)
276 {
277   /* Nothing to do */
278 }
279
280 void silc_schedule_internal_signals_block(SilcSchedule schedule, void *context)
281 {
282   /* Nothing to do */
283 }
284
285 void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
286                                             void *context)
287 {
288   /* Nothing to do */
289 }
290
291 #ifdef __WINSCW__
292 EXPORT_C const SilcScheduleOps schedule_ops =
293 #else
294 const SilcScheduleOps schedule_ops =
295 #endif /* __WINSCW__ */
296 {
297   silc_schedule_internal_init,
298   silc_schedule_internal_uninit,
299   silc_poll,
300   silc_schedule_internal_schedule_fd,
301   silc_schedule_internal_wakeup,
302   silc_schedule_internal_signal_register,
303   silc_schedule_internal_signal_unregister,
304   silc_schedule_internal_signals_call,
305   silc_schedule_internal_signals_block,
306   silc_schedule_internal_signals_unblock,
307 };
308
309 } /* extern "C" */