blob: 6203216f91bfeb70177029b2059094a48c629c7e [file] [log] [blame]
/* ------------------------------------------------------------------
* 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