Porting Toolkit to Symbian. It should work while some sporadic
[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 */
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     /* Wakeup scheduler */
117     timer->Cancel();
118     timer->After(0);
119     wake_signal = FALSE;
120
121     iStatus = KRequestPending;
122     SetActive();
123   }
124
125   virtual void DoCancel()
126   {
127
128   }
129
130   RThread thread;
131   TThreadId id;
132   SilcSymbianScheduler *timer;
133   unsigned int wake_signal  : 1;
134 };
135
136 extern "C" {
137
138 /* Symbian's silc_schedule call.  We start Active Scheduler here and start
139    our SILC Scheduler.  The calling thread will block here. */
140
141 void silc_schedule(SilcSchedule schedule)
142 {
143   SilcSymbianInternal internal = (SilcSymbianInternal)schedule->internal;
144   CActiveSchedulerWait *s;
145
146   SILC_LOG_DEBUG(("Running scheduler"));
147
148   /* Create Active Scheduler */
149   s = new CActiveSchedulerWait;
150   SILC_ASSERT(s);
151
152   /* Start SILC Scheduler */
153   internal->timer = new SilcSymbianScheduler;
154   SILC_ASSERT(internal->timer);
155   internal->timer->schedule = schedule;
156   internal->timer->s = s;
157   internal->wakeup = new SilcSymbianSchedulerWakeup;
158   SILC_ASSERT(internal->wakeup);
159   internal->wakeup->id = RThread().Id();
160   internal->wakeup->thread.Open(internal->wakeup->id);
161   internal->wakeup->timer = internal->timer;
162
163   /* Start Active Scheduler */
164   s->Start();
165
166   delete internal->wakeup;
167   delete internal->timer;
168   delete s;
169 }
170
171 int silc_poll(SilcSchedule schedule, void *context)
172 {
173   SilcSymbianInternal internal = (SilcSymbianInternal)context;
174   int timeout = -1;
175   TTime at_timeout;
176
177   /* When user is using silc_schedule_one we don't have our timer set,
178      so just return immediately. */
179   if (!internal->timer)
180     return 0;
181
182   /* Schedule next timeout */
183   if (schedule->has_timeout)
184     timeout = ((schedule->timeout.tv_sec * 1000) +
185                 (schedule->timeout.tv_usec / 1000));
186
187   if (!timeout)
188     return 0;
189
190   if (timeout == -1)
191     timeout = 0;
192
193   /* Set the timeout value */
194   at_timeout.HomeTime();
195   while (timeout > 2100 * 1000) {
196     at_timeout += (TTimeIntervalMicroSeconds32)(2100 * 1000 * 1000);
197     timeout -= (2100 * 1000);
198   }
199   at_timeout += (TTimeIntervalMicroSeconds32)timeout;
200
201   /* Schedule the timeout */
202   internal->timer->At(at_timeout);
203
204   /* Return special "ignore" value.  Causes the scheduler to just break
205      the scheduler iteration and return back to its caller. */
206   return -2;
207 }
208
209 SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,
210                                             void *context,
211                                             SilcTaskFd task,
212                                             SilcTaskEvent event_mask)
213 {
214   /* Nothing to do */
215   return TRUE;
216 }
217
218 void *silc_schedule_internal_init(SilcSchedule schedule,
219                                   void *app_context)
220 {
221   SilcSymbianInternal internal;
222
223   internal = (SilcSymbianInternal)silc_calloc(1, sizeof(*internal));
224   if (!internal)
225     return NULL;
226
227   return internal;
228 }
229
230 void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
231 {
232   SilcSymbianInternal internal = (SilcSymbianInternal)context;
233   silc_free(internal);
234 }
235
236 void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
237 {
238 #ifdef SILC_THREADS
239   SilcSymbianInternal internal = (SilcSymbianInternal)context;
240   TThreadId id;
241
242   if (!internal->timer)
243     return;
244
245   id = RThread().Id();
246   internal->wakeup->Wakeup(id);
247 #endif /* SILC_THREADS */
248 }
249
250 void silc_schedule_internal_signal_register(SilcSchedule schedule,
251                                             void *context,
252                                             SilcUInt32 sig,
253                                             SilcTaskCallback callback,
254                                             void *callback_context)
255 {
256   /* Nothing to do */
257 }
258
259 void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
260                                               void *context,
261                                               SilcUInt32 sig)
262 {
263   /* Nothing to do */
264 }
265
266 void silc_schedule_internal_signals_call(SilcSchedule schedule, void *context)
267 {
268   /* Nothing to do */
269 }
270
271 void silc_schedule_internal_signals_block(SilcSchedule schedule, void *context)
272 {
273   /* Nothing to do */
274 }
275
276 void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
277                                             void *context)
278 {
279   /* Nothing to do */
280 }
281
282 EXPORT_C const SilcScheduleOps schedule_ops =
283 {
284   silc_schedule_internal_init,
285   silc_schedule_internal_uninit,
286   silc_poll,
287   silc_schedule_internal_schedule_fd,
288   silc_schedule_internal_wakeup,
289   silc_schedule_internal_signal_register,
290   silc_schedule_internal_signal_unregister,
291   silc_schedule_internal_signals_call,
292   silc_schedule_internal_signals_block,
293   silc_schedule_internal_signals_unblock,
294 };
295
296 } /* extern "C" */