/*
Copyright (c) 2007-2013, John Ryland
*/
#include <event.h>
#include <keypad.h>
#include <corelayer.h>
#include <oslayer.h>
inline void getTimeOfDay(timeval* tv)
{
#if defined(ARCH_MacOSX64)
tv->tv_sec = sysGetTimeOfDay(tv, 0);
tv->tv_usec = 0;
#else
sysGetTimeOfDay(tv, 0);
#endif
}
timeval globalTimeOfDay = { -1, -1 };
typedef struct {
unsigned msTimeout;
unsigned repeat;
unsigned id;
void *data;
timeval next;
} Timer;
Timer timerPool[128];
unsigned timerPoolFlag[128];
unsigned timerPoolInit = 0;
void timerPool_Init()
{
unsigned i;
for (i = 0; i < 128; i++)
timerPoolFlag[i] = 0;
timerPoolInit = 1;
}
Timer *timerPool_AllocTimer()
{
unsigned i;
if (!timerPoolInit)
timerPool_Init();
for (i = 0; i < 128; i++) {
if ( timerPoolFlag[i] == 0 ) {
timerPoolFlag[i] = 1;
return &timerPool[i];
}
}
strPrint("Timer Pool Full\n"); // XXX
return 0;
}
void timerPool_FreeTimer(Timer *timer)
{
unsigned i;
if (!timerPoolInit)
timerPool_Init();
for (i = 0; i < 128; i++) {
if ( &timerPool[i] == timer ) {
timerPoolFlag[i] = 0;
return;
}
}
strPrint("Erroneous Timer, not from Timer Pool\n");
}
Timer *timerQueue[128];
unsigned timerQueueCount = 0;
unsigned timerQueueWatermark = 0;
unsigned lastTimerId = 0;
int timerQueue_CreateTimer(unsigned int msTimeout, unsigned int repeat, void *data)
{
// strPrint("creating timer\n");
if (timerQueueCount > 127) {
strPrint("Timer Queue Full, ignoring timer\n"); // XXX
} else {
Timer *timer = timerPool_AllocTimer();
// strPrint("allocated timer\n");
if (timer) {
lastTimerId++;
timer->id = lastTimerId;
timer->data = data;
// strPrint("created ");
// if (timer->data)
// strPrint((char*)timer->data);
// strPrint(" timer\n");
timer->msTimeout = msTimeout;
timer->repeat = repeat;
if ( globalTimeOfDay.tv_usec == -1 && globalTimeOfDay.tv_sec == -1 )
getTimeOfDay(&globalTimeOfDay);
timer->next = globalTimeOfDay;
// sysGetTimeOfDay(&timer->next, 0);
timer->next.tv_usec += msTimeout * 1000;
while (timer->next.tv_usec > 1000000) {
timer->next.tv_usec -= 1000000;
timer->next.tv_sec++;
}
timerQueue[timerQueueCount] = timer;
timerQueueCount++;
//START_PROFILE
if (timerQueueCount > timerQueueWatermark)
timerQueueWatermark = timerQueueCount;
//END_PROFILE
// strPrint("returning timer\n");
return timer->id;
}
}
return 0;
}
void timerQueue_DeleteTimer(int id)
{
// strPrint("deleting timer\n");
if (!timerQueueCount) {
strPrint("Timer Queue Empty, you shouldn't be calling this if it is empty\n");
} else {
unsigned i = 0;
while (timerQueue[i]->id != id && i < timerQueueCount)
i++;
if ( i < timerQueueCount ) {
Timer *tm = timerQueue[i];
timerQueueCount--;
while (i < timerQueueCount) {
timerQueue[i] = timerQueue[i + 1];
i++;
}
// strPrint("deleting ");
// if (tm->data)
// strPrint((char*)tm->data);
// strPrint(" timer\n");
timerPool_FreeTimer(tm);
}
}
}
Timer *timerQueue_FindNextTimer()
{
#define INT_MAX 2147483647
timeval tmSmallest = { INT_MAX, INT_MAX };
unsigned i, t = 0;
if (!timerQueueCount)
return 0;
for (i = 0; i < timerQueueCount; i++) {
if ((timerQueue[i]->next.tv_sec < tmSmallest.tv_sec) ||
((timerQueue[i]->next.tv_sec == tmSmallest.tv_sec) &&
(timerQueue[i]->next.tv_usec < tmSmallest.tv_usec)))
{
tmSmallest = timerQueue[i]->next;
t = i;
}
}
return timerQueue[t];
}
void fireTimer(Timer *timer)
{
Event te;
if (!timer) {
strPrint("Strange, timer is empty\n");
return;
}
te.type = TimerEventType;
te.event.timerEvent.id = timer->id;
te.event.timerEvent.data = timer->data;
strPrint("Adding timeout event\n");
eventQueue_AppendEvent(te);
if (timer->repeat) {
timer->next.tv_usec += timer->msTimeout * 1000;
while (timer->next.tv_usec > 1000000) {
timer->next.tv_usec -= 1000000;
timer->next.tv_sec++;
}
} else {
timerQueue_DeleteTimer(timer->id);
}
}
// Special case for firing timers that are now late
// Old non-repeating timers will generate an event
// Old repeating timers that missed their timeout do not.
void fireExpiredTimer(Timer *timer)
{
Event te;
if (!timer) {
strPrint("Strange, timer is empty\n");
return;
}
if (timer->repeat) {
timer->next.tv_usec += timer->msTimeout * 1000;
while (timer->next.tv_usec > 1000000) {
timer->next.tv_usec -= 1000000;
timer->next.tv_sec++;
}
} else {
te.type = TimerEventType;
te.event.timerEvent.id = timer->id;
te.event.timerEvent.data = timer->data;
eventQueue_AppendEvent(te);
timerQueue_DeleteTimer(timer->id);
}
}
Event eventPool[1024];
unsigned eventPoolFlag[1024];
unsigned eventPoolInit = 0;
void eventPool_Init()
{
unsigned i;
for (i = 0; i < 1024; i++)
eventPoolFlag[i] = 0;
eventPoolInit = 1;
}
Event *eventPool_AllocEvent()
{
unsigned i;
if (!eventPoolInit)
eventPool_Init();
for (i = 0; i < 1024; i++) {
if (eventPoolFlag[i] == 0) {
eventPoolFlag[i] = 1;
return &eventPool[i];
}
}
strPrint("Event Pool Full\n"); // XXX
return 0;
}
void eventPool_FreeEvent(Event *event)
{
unsigned i;
if (!eventPoolInit)
eventPool_Init();
for (i = 0; i < 1024; i++) {
if (&eventPool[i] == event) {
eventPoolFlag[i] = 0;
return;
}
}
strPrint("Erroneous Event, not from Event Pool\n");
}
Event *eventQueue[1024];
unsigned eventQueueCount = 0;
unsigned eventQueueWatermark = 0;
void eventQueue_AppendEvent(Event ev)
{
if (eventQueueCount > 1023) {
strPrint("Event Queue Full, ignoring event\n"); // XXX
return;
} else {
Event *event = eventPool_AllocEvent();
if (event) {
*event = ev;
eventQueue[eventQueueCount] = event;
eventQueueCount++;
//START_PROFILE
if (eventQueueCount > eventQueueWatermark)
eventQueueWatermark = eventQueueCount;
//END_PROFILE
}
}
}
Event eventQueue_TakeEvent(void)
{
Event event;
if (!eventQueueCount) {
strPrint("Event Queue Empty, you mustn't call this if it is empty\n");
} else {
Event *ev = eventQueue[0];
unsigned i;
for (i = 1; i <= eventQueueCount; i++)
eventQueue[i - 1] = eventQueue[i];
eventQueueCount--;
event = *ev;
eventPool_FreeEvent(ev);
}
return event;
}
extern int keyFd;
// TODO: This is a hack to move these out of the function
// I have no idea why it doesn't work putting them in the function
fd_set rfds, wfds, xfds;
void eventQueue_getEvents()
{
while (!eventQueueCount) {
unsigned highestFd = 0, ret;
Timer *timer = timerQueue_FindNextTimer();
timeval timeout, timeOfDay;
Event *ev;
int foundTimeout = (timer) ? 0 : 1;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&xfds);
FD_SET(keyFd, &rfds);
highestFd = keyFd;
getTimeOfDay(&timeOfDay);
/*
int day, month, year, hour, min, sec;
unsigned long val = timeOfDay.tv_sec;
sec = val % 60;
val /= 60;
min = val % 60;
val /= 60;
hour = val % 24;
val /= 24;
day = val % 30; // ### just quick hack
val /= 30;
month = val % 12; // ### just quick hack
val /= 12;
year = val + 1970;
// not real day/month/year, also no timezone adjustment in time
strPrintf("Date/Time: %i/%i/%i %i:%i:%i\n", day,month,year, hour,min,sec);
*/
/*
XXX could record here the latency of dispatch between timeOfDay and globalTimeOfDay
*/
while (!foundTimeout) {
timeout.tv_sec = timer->next.tv_sec - timeOfDay.tv_sec;
timeout.tv_usec = timer->next.tv_usec - timeOfDay.tv_usec;
while (timeout.tv_usec < 0) {
timeout.tv_usec += 1000000;
timeout.tv_sec--;
}
if (timeout.tv_sec < 0) {
// strPrint("Expired timeout, firing ");
// if (timer->data)
// strPrint((char*)timer->data);
// strPrint(" timer\n");
fireExpiredTimer(timer);
fireTimer(timer);
timer = timerQueue_FindNextTimer();
if (!timer)
foundTimeout = 1;
} else {
foundTimeout = 1;
}
}
// strPrint("Calling select\n");
ret = sysSelect(highestFd + 1, &rfds, &wfds, &xfds, (timer)?&timeout:0);
// strPrint("select returned\n");
getTimeOfDay(&globalTimeOfDay);
if (ret) {
if (FD_ISSET(keyFd, &rfds)) {
readKeyData();
}
} else {
// strPrint("Timeout, firing ");
// if (timer->data)
// strPrint((char*)timer->data);
// strPrint(" timer\n");
fireTimer(timer);
}
// #define LESS_SELECTS_AND_LONGER_HOLD_OF_CPU_TIME
#ifdef LESS_SELECTS_AND_LONGER_HOLD_OF_CPU_TIME
timer = timerQueue_FindNextTimer();
while (timer) {
timeout.tv_sec = timer->next.tv_sec - globalTimeOfDay.tv_sec;
timeout.tv_usec = timer->next.tv_usec - globalTimeOfDay.tv_usec;
while (timeout.tv_usec < 0) {
timeout.tv_usec += 1000000;
timeout.tv_sec--;
}
if (timeout.tv_sec < 0) {
fireTimer(timer);
timer = timerQueue_FindNextTimer();
} else {
timer = 0;
}
}
#endif
}
}
Event getNextEvent()
{
eventQueue_getEvents();
return eventQueue_TakeEvent();
}