/* silcsymbianscheduler.cpp Author: Pekka Riikonen 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 /* 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. This is called with scheduler locked. */ 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")); /* We need to synchronize with calls to Wakeup() */ silc_mutex_lock(schedule->lock); /* Wakeup scheduler */ timer->Cancel(); timer->After(0); wake_signal = FALSE; iStatus = KRequestPending; SetActive(); silc_mutex_unlock(schedule->lock); } virtual void DoCancel() { wake_signal = TRUE; } RThread thread; TThreadId id; SilcSymbianScheduler *timer; SilcSchedule schedule; 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; internal->wakeup->schedule = schedule; /* 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) return -2; /* Set the timeout value */ at_timeout.HomeTime(); while (timeout > 2100 * 1000) { at_timeout += (TTimeIntervalMicroSeconds32)(2100 * 1000 * 1000); timeout -= (2100 * 1000); } at_timeout += (TTimeIntervalMicroSeconds32)(timeout * 1000); /* Schedule the timeout */ if (internal->timer->IsActive()) internal->timer->Cancel(); 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 */ } #ifdef __WINSCW__ EXPORT_C const SilcScheduleOps schedule_ops = #else const SilcScheduleOps schedule_ops = #endif /* __WINSCW__ */ { 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" */