Porting Toolkit to Symbian. It should work while some sporadic
[silc.git] / lib / silcutil / symbian / silcsymbianscheduler.cpp
index f6f42cdcfb81526c7c9f1a327dcc2698dffb1848..fd1310244f84b9a6fa7aea84648686010e475a74 100644 (file)
-/*\r
-\r
-  silcsymbianschduler.cpp\r
-\r
-  Author: Pekka Riikonen <priikone@silcnet.org>\r
-\r
-  Copyright (C) 1998 - 2007 Pekka Riikonen\r
-\r
-  This program is free software; you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation; version 2 of the License.\r
-\r
-  This program is distributed in the hope that it will be useful,\r
-  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-  GNU General Public License for more details.\r
-\r
-*/\r
-\r
-/* On symbian the SILC Scheduler doesn't do anything.  All timeout tasks\r
-   are dispatched by the generic scheduler implementation.  Sockets and\r
-   file descriptors are dispatched automatically in their class\r
-   implementation, so adding FD Tasks on Symbian doesn't do anything.\r
-\r
-   This also means that on Symbian the SILC Scheduler always returns\r
-   immediately.  Because FD tasks use the Symbian scheduler the performance\r
-   is as good as it can be.  For timeout tasks the performance is not an\r
-   issue. */\r
-\r
-#include "silc.h"\r
-\r
-extern "C" {\r
-\r
-int silc_poll(SilcSchedule schedule, void *context)\r
-{\r
-  /* Return immediately, timeout. */\r
-  return 0;\r
-}\r
-\r
-SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,\r
-                                           void *context,\r
-                                           SilcTaskFd task,\r
-                                           SilcTaskEvent event_mask)\r
-{\r
-  /* Nothing to do */\r
-  return TRUE;\r
-}\r
-\r
-void *silc_schedule_internal_init(SilcSchedule schedule,\r
-                                 void *app_context)\r
-{\r
-  /* Nothing to do */\r
-  return (void *)1;\r
-}\r
-\r
-\r
-void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)\r
-{\r
-  /* Nothing to do */\r
-}\r
-\r
-void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)\r
-{\r
-  /* Nothing to do */\r
-}\r
-\r
-void silc_schedule_internal_signal_register(SilcSchedule schedule,\r
-                                           void *context,\r
-                                           SilcUInt32 sig,\r
-                                            SilcTaskCallback callback,\r
-                                            void *callback_context)\r
-{\r
-  /* Nothing to do */\r
-}\r
-\r
-void silc_schedule_internal_signal_unregister(SilcSchedule schedule,\r
-                                             void *context,\r
-                                             SilcUInt32 sig)\r
-{\r
-  /* Nothing to do */\r
-}\r
-\r
-void silc_schedule_internal_signals_call(SilcSchedule schedule, void *context)\r
-{\r
-  /* Nothing to do */\r
-}\r
-\r
-void silc_schedule_internal_signals_block(SilcSchedule schedule, void *context)\r
-{\r
-  /* Nothing to do */\r
-}\r
-\r
-void silc_schedule_internal_signals_unblock(SilcSchedule schedule,\r
-                                           void *context)\r
-{\r
-  /* Nothing to do */\r
-}\r
-\r
-EXPORT_C const SilcScheduleOps schedule_ops =\r
-{\r
-  silc_schedule_internal_init,\r
-  silc_schedule_internal_uninit,\r
-  silc_poll,\r
-  silc_schedule_internal_schedule_fd,\r
-  silc_schedule_internal_wakeup,\r
-  silc_schedule_internal_signal_register,\r
-  silc_schedule_internal_signal_unregister,\r
-  silc_schedule_internal_signals_call,\r
-  silc_schedule_internal_signals_block,\r
-  silc_schedule_internal_signals_unblock,\r
-};\r
-\r
-} /* extern "C" */
\ No newline at end of file
+/*
+
+  silcsymbianscheduler.cpp
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2007 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silc.h"
+#include <e32base.h>
+
+/* The SILC Scheduler for Symbian handles only timeout tasks.  Fd tasks are
+   not handled by the SILC Scheduler at all but are handled by the Symbian's
+   Active Scheduler.  Fd and socket stream callbacks are delivered back
+   to caller in their respective class implementatios.
+
+   The SILC Scheduler in Symbian works by creating CActiveSchedulerWait
+   when silc_schedule() is called.  This will block the calling thread just
+   like silc_schedule is supposed to do.  Under that Active Scheduler we
+   run our SilcSymbianScheduler timer which will handle the actual SILC
+   Scheduler by calling silc_schedule_one at correct times.  The timeout
+   values are selected by the SILC Scheduler itself when silc_schedule_one
+   is called.  After that call returns we go back to the Active Scheduler
+   to dispatch other active objects and to wait for next timeout.
+
+   Wakeup of the scheduler works by simply cancelling the outstanding timeout
+   and issuing a zero timeout to call the silc_schedule_one again.
+
+   If user directly calls silc_schedule_one it behaves same as on other
+   platforms. */
+
+class SilcSymbianScheduler;
+class SilcSymbianSchedulerWakeup;
+
+typedef struct {
+  SilcSymbianScheduler *timer;
+  SilcSymbianSchedulerWakeup *wakeup;
+} *SilcSymbianInternal;
+
+/* SILC scheduler timer class.  This handles the actual SILC Scheduler
+   by calling silc_schedule_one and scheduling the scheduler timeouts. */
+class SilcSymbianScheduler : public CTimer {
+public:
+  /* Constructor */
+  SilcSymbianScheduler() : CTimer(CActive::EPriorityStandard)
+  {
+    CTimer::ConstructL();
+    CActiveScheduler::Add(this);
+    After(0);
+  }
+
+  /* Destructor */
+  ~SilcSymbianScheduler()
+  {
+    Cancel();
+  }
+
+  /* Timeout callback */
+  virtual void RunL()
+  {
+    if (!silc_schedule_one(schedule, -1))
+      s->AsyncStop();
+  }
+
+  CActiveSchedulerWait *s;
+  SilcSchedule schedule;
+};
+
+/* Scheduler wakeup class */
+class SilcSymbianSchedulerWakeup : public CActive {
+public:
+  /* Constructor */
+  SilcSymbianSchedulerWakeup() : CActive(CActive::EPriorityStandard)
+  {
+    CActiveScheduler::Add(this);
+    iStatus = KRequestPending;
+    SetActive();
+  }
+
+  /* Destructor */
+  ~SilcSymbianSchedulerWakeup()
+  {
+    Cancel();
+  }
+
+  /* Wakeup */
+  void Wakeup(TThreadId thread_id)
+  {
+    if (wake_signal)
+      return;
+    wake_signal = TRUE;
+
+    TRequestStatus *status = &iStatus;
+    if (id != thread_id)
+      thread.RequestComplete(status, KErrNone);
+    else
+      User::RequestComplete(status, KErrNone);
+  }
+
+  /* Timeout callback */
+  virtual void RunL()
+  {
+    SILC_LOG_DEBUG(("Wakeup scheduler"));
+
+    /* Wakeup scheduler */
+    timer->Cancel();
+    timer->After(0);
+    wake_signal = FALSE;
+
+    iStatus = KRequestPending;
+    SetActive();
+  }
+
+  virtual void DoCancel()
+  {
+
+  }
+
+  RThread thread;
+  TThreadId id;
+  SilcSymbianScheduler *timer;
+  unsigned int wake_signal  : 1;
+};
+
+extern "C" {
+
+/* Symbian's silc_schedule call.  We start Active Scheduler here and start
+   our SILC Scheduler.  The calling thread will block here. */
+
+void silc_schedule(SilcSchedule schedule)
+{
+  SilcSymbianInternal internal = (SilcSymbianInternal)schedule->internal;
+  CActiveSchedulerWait *s;
+
+  SILC_LOG_DEBUG(("Running scheduler"));
+
+  /* Create Active Scheduler */
+  s = new CActiveSchedulerWait;
+  SILC_ASSERT(s);
+
+  /* Start SILC Scheduler */
+  internal->timer = new SilcSymbianScheduler;
+  SILC_ASSERT(internal->timer);
+  internal->timer->schedule = schedule;
+  internal->timer->s = s;
+  internal->wakeup = new SilcSymbianSchedulerWakeup;
+  SILC_ASSERT(internal->wakeup);
+  internal->wakeup->id = RThread().Id();
+  internal->wakeup->thread.Open(internal->wakeup->id);
+  internal->wakeup->timer = internal->timer;
+
+  /* Start Active Scheduler */
+  s->Start();
+
+  delete internal->wakeup;
+  delete internal->timer;
+  delete s;
+}
+
+int silc_poll(SilcSchedule schedule, void *context)
+{
+  SilcSymbianInternal internal = (SilcSymbianInternal)context;
+  int timeout = -1;
+  TTime at_timeout;
+
+  /* When user is using silc_schedule_one we don't have our timer set,
+     so just return immediately. */
+  if (!internal->timer)
+    return 0;
+
+  /* Schedule next timeout */
+  if (schedule->has_timeout)
+    timeout = ((schedule->timeout.tv_sec * 1000) +
+               (schedule->timeout.tv_usec / 1000));
+
+  if (!timeout)
+    return 0;
+
+  if (timeout == -1)
+    timeout = 0;
+
+  /* Set the timeout value */
+  at_timeout.HomeTime();
+  while (timeout > 2100 * 1000) {
+    at_timeout += (TTimeIntervalMicroSeconds32)(2100 * 1000 * 1000);
+    timeout -= (2100 * 1000);
+  }
+  at_timeout += (TTimeIntervalMicroSeconds32)timeout;
+
+  /* Schedule the timeout */
+  internal->timer->At(at_timeout);
+
+  /* Return special "ignore" value.  Causes the scheduler to just break
+     the scheduler iteration and return back to its caller. */
+  return -2;
+}
+
+SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,
+                                           void *context,
+                                           SilcTaskFd task,
+                                           SilcTaskEvent event_mask)
+{
+  /* Nothing to do */
+  return TRUE;
+}
+
+void *silc_schedule_internal_init(SilcSchedule schedule,
+                                 void *app_context)
+{
+  SilcSymbianInternal internal;
+
+  internal = (SilcSymbianInternal)silc_calloc(1, sizeof(*internal));
+  if (!internal)
+    return NULL;
+
+  return internal;
+}
+
+void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
+{
+  SilcSymbianInternal internal = (SilcSymbianInternal)context;
+  silc_free(internal);
+}
+
+void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
+{
+#ifdef SILC_THREADS
+  SilcSymbianInternal internal = (SilcSymbianInternal)context;
+  TThreadId id;
+
+  if (!internal->timer)
+    return;
+
+  id = RThread().Id();
+  internal->wakeup->Wakeup(id);
+#endif /* SILC_THREADS */
+}
+
+void silc_schedule_internal_signal_register(SilcSchedule schedule,
+                                           void *context,
+                                           SilcUInt32 sig,
+                                            SilcTaskCallback callback,
+                                            void *callback_context)
+{
+  /* Nothing to do */
+}
+
+void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
+                                             void *context,
+                                             SilcUInt32 sig)
+{
+  /* Nothing to do */
+}
+
+void silc_schedule_internal_signals_call(SilcSchedule schedule, void *context)
+{
+  /* Nothing to do */
+}
+
+void silc_schedule_internal_signals_block(SilcSchedule schedule, void *context)
+{
+  /* Nothing to do */
+}
+
+void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
+                                           void *context)
+{
+  /* Nothing to do */
+}
+
+EXPORT_C const SilcScheduleOps schedule_ops =
+{
+  silc_schedule_internal_init,
+  silc_schedule_internal_uninit,
+  silc_poll,
+  silc_schedule_internal_schedule_fd,
+  silc_schedule_internal_wakeup,
+  silc_schedule_internal_signal_register,
+  silc_schedule_internal_signal_unregister,
+  silc_schedule_internal_signals_call,
+  silc_schedule_internal_signals_block,
+  silc_schedule_internal_signals_unblock,
+};
+
+} /* extern "C" */