#include "protoTimer.h"
#include "protoDebug.h"
#include <stdio.h>
ProtoTimer::ProtoTimer()
: listener(NULL), interval(1.0), repeat(0), repeat_count(0), mgr(NULL)
#ifndef _SORTED_TIMERS
,prev(NULL), next(NULL)
#endif {
}
ProtoTimer::~ProtoTimer()
{
if (IsActive()) Deactivate();
if (listener)
{
delete listener;
listener = NULL;
}
}
bool ProtoTimer::Reschedule()
{
ASSERT(IsActive());
if (IsActive())
{
ProtoTimerMgr* timerMgr = mgr;
bool updatePending = timerMgr->update_pending;
timerMgr->update_pending = true;
timerMgr->DeactivateTimer(*this);
timerMgr->update_pending = updatePending;
timerMgr->ActivateTimer(*this);
return true;
}
else
{
PLOG(PL_ERROR, "ProtoTimer::Reschedule() error: timer not active\n");
return false;
}
}
void ProtoTimer::Scale(double factor)
{
if (IsActive())
{
double newInterval = factor*interval;
double timeRemaining = GetTimeRemaining();
if (timeRemaining > 0.0)
{
interval = factor*timeRemaining;
int repeatCountSaved = repeat_count;
Reschedule();
repeat_count = repeatCountSaved;
}
interval = newInterval;
}
else
{
interval *= factor;
}
}
void ProtoTimer::Deactivate()
{
ASSERT(IsActive());
mgr->DeactivateTimer(*this);
}
double ProtoTimer::GetTimeRemaining() const
{
if (NULL != mgr)
{
ProtoTime currentTime;
mgr->GetCurrentProtoTime(currentTime);
double timeRemaining = ProtoTime::Delta(timeout, currentTime);
if (timeRemaining < 0.0) timeRemaining = 0.0;
return timeRemaining;
}
else
{
return -1.0;
}
}
ProtoTimerMgr::ProtoTimerMgr()
: update_pending(false), timeout_scheduled(false),
#ifdef _SORTED_TIMERS
timer_list_count(0),
#else
long_head(NULL), long_tail(NULL), short_head(NULL), short_tail(NULL),
#endif invoked_timer(NULL)
{
pulse_timer.SetListener(this, &ProtoTimerMgr::OnPulseTimeout);
pulse_timer.SetInterval(1.0);
pulse_timer.SetRepeat(-1);
}
ProtoTimerMgr::~ProtoTimerMgr()
{
}
void ProtoTimerMgr::GetSystemTime(struct timeval& currentTime)
{
::ProtoSystemTime(currentTime);
}
const double ProtoTimerMgr::PRECISION_TIME_THRESHOLD = 8.0;
void ProtoTimerMgr::OnSystemTimeout()
{
timeout_scheduled = false;
bool updateStatus = update_pending;
update_pending = true;
ProtoTimer* next = GetShortHead();
ProtoTime now;
GetCurrentProtoTime(now);
while (next)
{
double delta = ProtoTime::Delta(next->timeout, now);
if (delta < 1.0e-06)
{
invoked_timer = next;
next->DoTimeout();
if(invoked_timer== next)
{
if (next->IsActive())
{
RemoveShortTimer(*next);
int repeatCount = next->repeat_count;
if (0 != repeatCount)
{
ReactivateTimer(*next, now);
if (repeatCount > 0) repeatCount--;
next->repeat_count = repeatCount;
}
}
}
invoked_timer = NULL;
next = GetShortHead();
}
else
{
next = NULL;
}
}
update_pending = updateStatus;
if (!updateStatus) Update();
}
bool ProtoTimerMgr::OnPulseTimeout(ProtoTimer& )
{
ProtoTimer* next = GetLongHead();
pulse_mark += 1.0;
while (NULL != next)
{
double delta = ProtoTime::Delta(next->timeout, pulse_mark);
if (delta < PRECISION_TIME_THRESHOLD)
{
RemoveLongTimer(*next);
GetCurrentProtoTime(next->timeout);
if (delta >= 0.0)
next->timeout += delta;
else if (delta < -0.100)
PLOG(PL_DEBUG, "ProtoTimerMgr: Warning! real time failure interval:%lf (delta:%lf)\n",
next->GetInterval(), delta);
InsertShortTimer(*next);
next = GetLongHead();
}
else
{
break;
}
}
if (NULL == GetLongHead())
{
DeactivateTimer(pulse_timer);
return false;
}
else
{
return true;
}
}
void ProtoTimerMgr::ActivateTimer(ProtoTimer& theTimer)
{
ASSERT(!theTimer.IsActive());
double timerInterval = theTimer.GetInterval();
if (PRECISION_TIME_THRESHOLD > timerInterval)
{
GetCurrentProtoTime(theTimer.timeout);
theTimer.timeout += timerInterval;
InsertShortTimer(theTimer);
}
else
{
if (!pulse_timer.IsActive())
{
GetCurrentProtoTime(pulse_mark);
bool updateStatus = update_pending;
update_pending = true;
ActivateTimer(pulse_timer);
update_pending = updateStatus;
}
theTimer.timeout = pulse_mark;
double delta = timerInterval + 1.0 - pulse_timer.GetTimeRemaining();
ASSERT(delta >= 0.0);
theTimer.timeout += delta;
InsertLongTimer(theTimer);
}
theTimer.repeat_count = theTimer.repeat;
if (!update_pending) Update();
}
void ProtoTimerMgr::ReactivateTimer(ProtoTimer& theTimer, const ProtoTime& now)
{
double timerInterval = theTimer.GetInterval();
if (PRECISION_TIME_THRESHOLD > timerInterval)
{
theTimer.timeout += timerInterval;
double delta = ProtoTime::Delta(theTimer.timeout, now);
if (delta < -0.100 )
{
GetCurrentProtoTime(theTimer.timeout);
PLOG(PL_DEBUG, "ProtoTimerMgr: Warning! real time failure interval:%lf (delta:%lf)\n",
timerInterval, delta);
}
InsertShortTimer(theTimer);
}
else
{
if (!pulse_timer.IsActive())
{
GetCurrentProtoTime(pulse_mark);
bool updateStatus = update_pending;
update_pending = true;
ActivateTimer(pulse_timer);
update_pending = updateStatus;
}
GetPulseTime(theTimer.timeout);
theTimer.timeout += timerInterval;
InsertLongTimer(theTimer);
}
if (!update_pending) Update();
}
void ProtoTimerMgr::DeactivateTimer(ProtoTimer& theTimer)
{
if (theTimer.mgr == this)
{
if (theTimer.is_precise)
{
if (&theTimer == invoked_timer)
invoked_timer = NULL;
RemoveShortTimer(theTimer);
}
else
{
RemoveLongTimer(theTimer);
if (NULL == GetLongHead())
{
bool updateStatus = update_pending;
update_pending = true;
DeactivateTimer(pulse_timer);
update_pending = updateStatus;
}
}
if (!update_pending) Update();
}
}
void ProtoTimerMgr::Update()
{
if (NULL == GetShortHead())
{
if (timeout_scheduled)
{
if (!UpdateSystemTimer(ProtoTimer::REMOVE, -1.0))
PLOG(PL_ERROR, "ProtoTimerMgr::Update() error: scheduled system timeout REMOVE failure\n");
timeout_scheduled = false;
}
}
else if (timeout_scheduled)
{
if (scheduled_timeout != GetShortHead()->timeout)
{
if (UpdateSystemTimer(ProtoTimer::MODIFY, GetShortHead()->GetTimeRemaining()))
{
scheduled_timeout = GetShortHead()->timeout;
}
else {
PLOG(PL_ERROR, "ProtoTimerMgr::Update() error: scheduled system timeout MODIFY failure\n");
timeout_scheduled = false; }
}
}
else
{
if (UpdateSystemTimer(ProtoTimer::INSTALL, GetShortHead()->GetTimeRemaining()))
{
scheduled_timeout = GetShortHead()->timeout;
timeout_scheduled = true;
}
else
{
PLOG(PL_ERROR, "ProtoTimerMgr::Update() error: scheduled system timeout INSTALL failure\n");
}
}
}
#ifdef _SORTED_TIMERS
void ProtoTimerMgr::InsertShortTimer(ProtoTimer& theTimer)
{
theTimer.mgr = this;
theTimer.is_precise = true;
const int LINKED_LIST_MAX = 16;
bool addToList;
if (timer_list_count < LINKED_LIST_MAX)
{
if (timer_table.IsEmpty() || (theTimer.timeout <= timer_table.GetHead()->timeout))
{
addToList = true;
timer_list_count++;
}
else
{
addToList = false;
}
}
else if (LINKED_LIST_MAX > 0)
{
ProtoTimer* listTail = timer_list.GetTail();
if (theTimer.timeout < listTail->timeout)
{
timer_list.Remove(*listTail);
listTail->UpdateKey();
timer_table.Insert(*listTail);
addToList = true;
}
else
{
addToList = false;
}
}
else
{
addToList = false;
}
if (addToList)
{
ProtoTimerList::Iterator iterator(timer_list);
ProtoTimer* nextTimer;
while (true)
{
nextTimer = iterator.GetNextItem();
if ((NULL == nextTimer) || (theTimer.timeout <= nextTimer->timeout))
break;
}
theTimer.InvalidateKey();
if (NULL == nextTimer)
timer_list.Append(theTimer);
else
timer_list.Insert(theTimer, *nextTimer);
}
else
{
theTimer.UpdateKey();
timer_table.Insert(theTimer);
}
while (timer_list_count < LINKED_LIST_MAX)
{
ProtoTimer* next = timer_table.RemoveHead();
if (NULL != next)
{
next->InvalidateKey();
timer_list.Append(*next);
timer_list_count++;
}
else
{
break;
}
}
}
void ProtoTimerMgr::RemoveShortTimer(ProtoTimer& theTimer)
{
if (theTimer.KeyIsValid())
{
timer_table.Remove(theTimer);
}
else
{
timer_list.Remove(theTimer);
timer_list_count--;
}
theTimer.mgr = NULL;
}
void ProtoTimerMgr::InsertLongTimer(ProtoTimer& theTimer)
{
theTimer.mgr = this;
theTimer.is_precise = false;
theTimer.UpdateKey();
long_timer_table.Insert(theTimer);
}
void ProtoTimerMgr::RemoveLongTimer(ProtoTimer& theTimer)
{
long_timer_table.Remove(theTimer);
theTimer.mgr = NULL;
}
#else
void ProtoTimerMgr::InsertShortTimer(ProtoTimer& theTimer)
{
unsigned breakCount = 0;
ProtoTimer* next = short_head;
theTimer.mgr = this;
theTimer.is_precise = true;
while(next)
{
double delta = ProtoTime::Delta(theTimer.timeout, next->timeout);
if (delta <= 0.0)
{
theTimer.next = next;
if(NULL != (theTimer.prev = next->prev))
theTimer.prev->next = &theTimer;
else
short_head = &theTimer;
next->prev = &theTimer;
return;
}
else
{
next = next->next;
}
breakCount++;
if(breakCount == 10) {
if(InsertShortTimerReverse(theTimer))
{
return;
}
}
}
if (NULL != (theTimer.prev = short_tail))
short_tail->next = &theTimer;
else
short_head = &theTimer;
short_tail = &theTimer;
theTimer.next = NULL;
}
bool ProtoTimerMgr::InsertShortTimerReverse(ProtoTimer& theTimer)
{
unsigned breakCount = 0;
ProtoTimer* prev = short_tail;
theTimer.mgr = this;
theTimer.is_precise = true;
while(prev)
{
double delta = ProtoTime::Delta(theTimer.timeout, prev->timeout);
if (delta > 0.0)
{
if(NULL == (theTimer.next = prev->next))
short_tail = &theTimer;
else
theTimer.next->prev = &theTimer;
theTimer.prev = prev;
prev->next = &theTimer;
return true;
}
else
{
prev = prev->prev;
}
breakCount++;
if(breakCount == 10)
{
return false;
}
}
if (NULL == (theTimer.next = short_head))
short_tail = &theTimer;
else
short_head->prev = &theTimer;
short_head = &theTimer;
theTimer.prev = NULL;
return true;
}
void ProtoTimerMgr::RemoveShortTimer(ProtoTimer& theTimer)
{
if (theTimer.prev)
theTimer.prev->next = theTimer.next;
else
short_head = theTimer.next;
if (theTimer.next)
theTimer.next->prev = theTimer.prev;
else
short_tail = theTimer.prev;
theTimer.mgr = NULL;
}
void ProtoTimerMgr::InsertLongTimer(ProtoTimer& theTimer)
{
unsigned breakCount = 0;
ProtoTimer* next = long_head;
theTimer.mgr = this;
theTimer.is_precise = false;
while(next)
{
double delta = ProtoTime::Delta(theTimer.timeout, next->timeout);
if (delta <= 0.0)
{
theTimer.next = next;
if((theTimer.prev = next->prev))
theTimer.prev->next = &theTimer;
else
long_head = &theTimer;
next->prev = &theTimer;
return;
}
else
{
next = next->next;
}
breakCount++;
if(breakCount == 10) {
if(InsertLongTimerReverse(theTimer))
{
return;
}
}
}
if ((theTimer.prev = long_tail))
long_tail->next = &theTimer;
else
long_head = &theTimer;
long_tail = &theTimer;
theTimer.next = NULL;
}
bool ProtoTimerMgr::InsertLongTimerReverse(ProtoTimer& theTimer)
{
unsigned breakCount = 0;
ProtoTimer* prev = long_tail;
theTimer.mgr = this;
theTimer.is_precise = false;
while(prev)
{
double delta = ProtoTime::Delta(theTimer.timeout, prev->timeout);
if (delta > 0.0)
{
if(NULL == (theTimer.next = prev->next))
long_tail = &theTimer;
else
theTimer.next->prev = &theTimer;
theTimer.prev = prev;
prev->next = &theTimer;
return true;
}
else
{
prev = prev->prev;
}
breakCount++;
if(breakCount == 10)
{
return false;
}
}
if (NULL == (theTimer.next = long_head))
long_tail = &theTimer;
else
long_head->prev = &theTimer;
long_head = &theTimer;
theTimer.prev = NULL;
return true;
}
void ProtoTimerMgr::RemoveLongTimer(ProtoTimer& theTimer)
{
if (theTimer.prev)
theTimer.prev->next = theTimer.next;
else
long_head = theTimer.next;
if (theTimer.next)
theTimer.next->prev = theTimer.prev;
else
long_tail = theTimer.prev;
theTimer.mgr = NULL;
}
#endif