| /* ------------------------------------------------------------------ |
| * Copyright (C) 1998-2009 PacketVideo |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| * express or implied. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * ------------------------------------------------------------------- |
| */ |
| #ifndef OSCL_TIMER_H_INCLUDED |
| #define OSCL_TIMER_H_INCLUDED |
| |
| #ifndef OSCL_BASE_H_INCLUDED |
| #include "oscl_base.h" |
| #endif |
| |
| #ifndef OSCLCONFIG_UTIL_H_INCLUDED |
| #include "osclconfig_util.h" |
| #endif |
| |
| #ifndef OSCL_VECTOR_H_INCLUDED |
| #include "oscl_vector.h" |
| #endif |
| |
| #ifndef OSCL_TICKCOUNT_H_INCLUDED |
| #include "oscl_tickcount.h" |
| #endif |
| |
| #ifndef OSCL_RAND_H_INCLUDED |
| #include "oscl_rand.h" |
| #endif |
| |
| #ifndef OSCL_SCHEDULER_AO_H_INCLUDED |
| #include "oscl_scheduler_ao.h" |
| #endif |
| |
| |
| /** |
| * The observer class to receive timeout callbacks |
| */ |
| class OsclTimerObserver |
| { |
| public: |
| /** |
| * This function will be called when the timer associated |
| * with this observer is executed |
| * |
| * @param timerID The ID given at timer request. |
| * @param timeoutInfo |
| * Any extra info given at timer request. |
| */ |
| virtual void TimeoutOccurred(int32 timerID, int32 timeoutInfo) = 0; |
| |
| virtual ~OsclTimerObserver() {} |
| }; |
| |
| /** |
| * A timer class for scheduling one or more timeout events. |
| * The timeout event will trigger a callback to an observer |
| * class. |
| */ |
| template<class Alloc> |
| class OsclTimer ; |
| |
| class CallbackTimerObserver |
| { |
| public: |
| virtual void TimerBaseElapsed() = 0; |
| virtual ~CallbackTimerObserver() {} |
| }; |
| |
| template<class Alloc> |
| class CallbackTimer: public OsclTimerObject |
| { |
| public: |
| CallbackTimer(CallbackTimerObserver& aContainer, const char *name, int32 aPriority = OsclActiveObject::EPriorityNominal) |
| : OsclTimerObject(aPriority, name) |
| { |
| iContainer = &aContainer; |
| AddToScheduler(); |
| } |
| ~CallbackTimer() |
| { |
| RemoveFromScheduler(); |
| } |
| void Run() |
| { |
| if (Status() == OSCL_REQUEST_ERR_NONE) |
| iContainer->TimerBaseElapsed(); |
| } |
| private: |
| CallbackTimerObserver *iContainer; |
| }; |
| |
| |
| template<class Alloc> |
| class OsclTimer : public CallbackTimerObserver |
| { |
| public: |
| |
| typedef CallbackTimer<Alloc> callback_timer_type; |
| |
| /** |
| * Constructor |
| * |
| * @param frequency The frequency of the timer in cycles/second. A value of |
| * 1 means the timer will cycle in 1 second intervals. |
| */ |
| OsclTimer(const char *name, uint32 frequency = 1, int32 priority = OsclActiveObject::EPriorityNominal); |
| virtual ~OsclTimer(); |
| |
| /** |
| * Set the global observer. Each timer can request a local |
| * observer, which if set overrides the global observer. |
| * |
| * @param obs observer object. |
| */ |
| void SetObserver(OsclTimerObserver *obs) |
| { |
| iObserver = obs; |
| } |
| /** |
| * Set the frequency of the timer in cycles/second. |
| * |
| * @param frequency A value of 1 means the timer will cycle in one second |
| * intervals, 1000 means millisecond intervals, etc. |
| */ |
| void SetFrequency(uint32 frequency); |
| |
| /** |
| * Set the exact frequency of the timer in microsecond. |
| * |
| * @param frequency A value of 1 means the timer will cycle in one microsecond |
| * intervals, 1000 means millisecond intervals, etc. |
| */ |
| void SetExactFrequency(uint32 frequency); |
| |
| /** |
| * Request a timer |
| * |
| * @param timerID used to identify the timer for cancellation. This value |
| * will be returned as part of the timeout event. |
| * @param timeoutInfo |
| * for user info. Returned to the observer on a timeout event |
| * @param cycles the number of cycles to wait before a timeout event. If |
| * the timer frequency is 1 and the cycles are set to 2, then |
| * the timeout event will occur in 2 seconds. |
| * @param obs a local observer object to be called on a timeout event. |
| * This observer overides the global observer if set. |
| */ |
| void Request(int32 timerID, int32 timeoutInfo, int32 cycles, OsclTimerObserver *obs = 0, bool recurring = 0); |
| /** |
| * Cancel a timer |
| * |
| * @param timerID used to identify the timer to cancel. |
| * @param timeoutInfo |
| * if not set to -1, this value will be used as additional |
| * matching criteria to cancel a timer. |
| */ |
| void Cancel(int32 timerID, int32 timeoutInfo = -1); |
| /** |
| * Cancel all pending timers. |
| */ |
| void Clear(); |
| |
| private: |
| //Note: the timer needs to be a new'd object so that |
| //the CBase construction zeros the memory. |
| callback_timer_type *iTimer; |
| |
| typedef struct _TimerEntry |
| { |
| int32 iCounter ; |
| int32 iTimerID ; |
| int32 iParam ; |
| OsclTimerObserver *iObserver; |
| bool iRecurring; |
| int32 iOrigCounter; |
| } TimerEntry; |
| |
| typedef TimerEntry entry_type; |
| typedef Oscl_Vector<entry_type*, Alloc> entries_type; |
| typedef typename entries_type::iterator entries_type_iterator; |
| |
| OsclTimerObserver *iObserver; |
| entries_type iEntries; |
| entries_type iEntriesWaitingToAdd; |
| entries_type iEntriesWaitingToCancel; |
| Oscl_TAlloc<entry_type, Alloc> iEntryAllocator; |
| |
| bool iInCallback; |
| |
| uint32 iCyclePeriod; |
| uint32 iTickCountPeriod; |
| uint32 iExpectedTimeout; |
| |
| protected: |
| void TimerBaseElapsed(); |
| friend class CallbackTimer<Alloc>; |
| }; |
| |
| template<class Alloc> |
| OsclTimer<Alloc>::OsclTimer(const char *name, uint32 frequency, int32 priority) : |
| iObserver(0) |
| , iInCallback(false) |
| , iTickCountPeriod(0) |
| , iExpectedTimeout(0) |
| { |
| //use the allocator with placement 'new' |
| Alloc alloc; |
| iTimer = OSCL_PLACEMENT_NEW(alloc.ALLOCATE(sizeof(CallbackTimer<Alloc>)), CallbackTimer<Alloc>(*this, name, priority)); |
| SetFrequency(frequency); |
| } |
| |
| template<class Alloc> |
| OsclTimer<Alloc>::~OsclTimer() |
| { |
| // Make sure we're cancelled |
| if (iTimer) |
| iTimer->Cancel(); |
| if (iTimer) |
| { |
| iTimer->OSCL_TEMPLATED_DESTRUCTOR_CALL(callback_timer_type, CallbackTimer); |
| Alloc alloc; |
| alloc.deallocate(iTimer); |
| } |
| iTimer = NULL; |
| |
| for (entries_type_iterator it = iEntries.begin(); it != iEntries.end(); it++) |
| { |
| iEntryAllocator.deallocate(*it); |
| } |
| } |
| |
| template<class Alloc> |
| void OsclTimer<Alloc>::SetFrequency(uint32 frequency) |
| { |
| // timer takes microseconds |
| iCyclePeriod = 1000000 / frequency; |
| // get tick count period |
| iTickCountPeriod = OsclTickCount::TickCountPeriod(); |
| } |
| |
| template<class Alloc> |
| void OsclTimer<Alloc>::SetExactFrequency(uint32 frequency) |
| { |
| // timer takes microseconds |
| iCyclePeriod = frequency; |
| // get tick count period |
| iTickCountPeriod = OsclTickCount::TickCountPeriod(); |
| } |
| |
| // Request a timer |
| template<class Alloc> |
| void OsclTimer<Alloc>::Request(int32 timerID, int32 param, int32 cycles, OsclTimerObserver *obs, bool recurring) |
| { |
| |
| // add to list of timers |
| entry_type *entry = iEntryAllocator.ALLOCATE(1); |
| entry->iTimerID = timerID; |
| entry->iParam = param; |
| entry->iCounter = cycles; |
| entry->iObserver = obs; |
| entry->iRecurring = recurring; |
| entry->iOrigCounter = entry->iCounter; |
| |
| // if the request is called inside of a callback, then we must add it later |
| if (iInCallback) |
| { |
| iEntriesWaitingToAdd.push_back(entry); |
| return; |
| } |
| |
| iEntries.push_back(entry); |
| |
| if (iTimer) |
| { |
| iTimer->RunIfNotReady(iCyclePeriod); |
| } |
| |
| if (iExpectedTimeout == 0) |
| { |
| iExpectedTimeout = (OsclTickCount::TickCount() * iTickCountPeriod) + iCyclePeriod; |
| } |
| } |
| |
| // Cancel a timer |
| template<class Alloc> |
| void OsclTimer<Alloc>::Cancel(int32 timerID, int32 param) |
| { |
| |
| if (iInCallback) |
| { |
| // add to list of timers |
| entry_type *entry = iEntryAllocator.ALLOCATE(1); |
| entry->iTimerID = timerID; |
| entry->iParam = param; |
| |
| iEntriesWaitingToCancel.push_back(entry); |
| return; |
| } |
| |
| // remove from list of timers |
| for (entries_type_iterator it = iEntries.begin(); it != iEntries.end(); it++) |
| { |
| if ((*it)->iTimerID == timerID) |
| { |
| // make sure the param matches unless it is not specified (-1) |
| if ((*it)->iParam == param || param == -1) |
| { |
| iEntryAllocator.deallocate(*it); |
| iEntries.erase(it); |
| return; |
| } |
| } |
| } |
| } |
| |
| // Clear all waiting timers |
| template<class Alloc> |
| void OsclTimer<Alloc>::Clear() |
| { |
| for (entries_type_iterator it = iEntries.begin(); it != iEntries.end(); it++) |
| { |
| iEntryAllocator.deallocate(*it); |
| } |
| iEntries.clear(); |
| } |
| |
| template<class Alloc> |
| void OsclTimer<Alloc>::TimerBaseElapsed() |
| { |
| uint8 expiredFound = 0; |
| |
| { |
| // call all whose timers have expired |
| for (entries_type_iterator it = iEntries.begin(); it != iEntries.end(); it++) |
| { |
| entry_type *entry = (*it); |
| if (--(entry->iCounter) <= 0) |
| { |
| if (!entry->iRecurring) expiredFound = 1; |
| if (entry->iRecurring) entry->iCounter = entry->iOrigCounter; |
| |
| // use local observer if it exists, otherwise use global observer |
| OsclTimerObserver *obs = (entry->iObserver ? entry->iObserver : iObserver); |
| if (obs) |
| { |
| iInCallback = true; |
| obs->TimeoutOccurred(entry->iTimerID, entry->iParam); |
| iInCallback = false; |
| } |
| } |
| } |
| } |
| |
| // remove from list all whose timers have expired |
| while (expiredFound) |
| { |
| expiredFound = 0; |
| for (entries_type_iterator it = iEntries.begin(); it != iEntries.end(); it++) |
| { |
| entry_type *entry = (*it); |
| if (entry->iCounter <= 0) |
| { |
| expiredFound = 1; |
| iEntryAllocator.deallocate(entry); |
| iEntries.erase(it); |
| break; |
| } |
| } |
| } |
| |
| { |
| // if any timers were cancelled in the callback, process them now |
| for (entries_type_iterator it = iEntriesWaitingToCancel.begin(); it != iEntriesWaitingToCancel.end(); it++) |
| { |
| entry_type *entry = (*it); |
| Cancel(entry->iTimerID, entry->iParam); |
| iEntryAllocator.deallocate(entry); |
| } |
| iEntriesWaitingToCancel.clear(); |
| } |
| |
| { |
| // if any timers were requested in the callback, process them now |
| for (entries_type_iterator it = iEntriesWaitingToAdd.begin(); it != iEntriesWaitingToAdd.end(); it++) |
| { |
| entry_type *entry = (*it); |
| Request(entry->iTimerID, entry->iParam, entry->iCounter, entry->iObserver); |
| iEntryAllocator.deallocate(entry); |
| } |
| iEntriesWaitingToAdd.clear(); |
| } |
| |
| if (!iEntries.empty()) |
| { |
| // adjust for the jitter |
| uint32 time = OsclTickCount::TickCount() * iTickCountPeriod; |
| int32 jitter = time - iExpectedTimeout; |
| int32 waitperiod = iCyclePeriod - jitter; |
| |
| // currently there is some problem on the phone if we send |
| // in real-time rather than with a slower (growing delay) H.223 mux output |
| // if jitter is too large in either direction, start over |
| if ((uint)OSCL_ABS(jitter) > iCyclePeriod) |
| { |
| iExpectedTimeout = time; |
| } |
| else |
| { |
| iExpectedTimeout += iCyclePeriod; |
| } |
| |
| waitperiod = OSCL_MAX(waitperiod, 0); |
| |
| if (iTimer) |
| { |
| iTimer->RunIfNotReady(waitperiod); |
| } |
| } |
| else |
| { |
| iExpectedTimeout = 0; |
| } |
| } |
| |
| |
| |
| #endif |