diff options
Diffstat (limited to 'src/libs/callback')
-rw-r--r-- | src/libs/callback/Makeinfo | 2 | ||||
-rw-r--r-- | src/libs/callback/alarm.c | 177 | ||||
-rw-r--r-- | src/libs/callback/alarm.h | 56 | ||||
-rw-r--r-- | src/libs/callback/async.c | 56 | ||||
-rw-r--r-- | src/libs/callback/async.h | 28 | ||||
-rw-r--r-- | src/libs/callback/callback.c | 193 | ||||
-rw-r--r-- | src/libs/callback/callback.h | 43 |
7 files changed, 555 insertions, 0 deletions
diff --git a/src/libs/callback/Makeinfo b/src/libs/callback/Makeinfo new file mode 100644 index 0000000..8842cba --- /dev/null +++ b/src/libs/callback/Makeinfo @@ -0,0 +1,2 @@ +uqm_CFILES="alarm.c async.c callback.c" +uqm_HFILES="alarm.h async.h callback.h" diff --git a/src/libs/callback/alarm.c b/src/libs/callback/alarm.c new file mode 100644 index 0000000..c9bd6ce --- /dev/null +++ b/src/libs/callback/alarm.c @@ -0,0 +1,177 @@ +/* + * Copyright 2006 Serge van den Boom <svdb@stack.nl> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "alarm.h" + +#include SDL_INCLUDE(SDL.h) +#include "libs/heap.h" + +#include <assert.h> +#include <stdlib.h> + + +Heap *alarmHeap; + + +static inline Alarm * +Alarm_alloc(void) { + return malloc(sizeof (Alarm)); +} + +static inline void +Alarm_free(Alarm *alarm) { + free(alarm); +} + +static inline int +AlarmTime_compare(const AlarmTime t1, const AlarmTime t2) { + if (t1 < t2) + return -1; + if (t1 > t2) + return 1; + return 0; +} + +static int +Alarm_compare(const Alarm *a1, const Alarm *a2) { + return AlarmTime_compare(a1->time, a2->time); +} + +void +Alarm_init(void) { + assert(alarmHeap == NULL); + alarmHeap = Heap_new((HeapValue_Comparator) Alarm_compare, + 4, 4, 0.8); +} + +void +Alarm_uninit(void) { + assert(alarmHeap != NULL); + + while (Heap_hasMore(alarmHeap)) { + Alarm *alarm = (Alarm *) Heap_pop(alarmHeap); + Alarm_free(alarm); + } + Heap_delete(alarmHeap); + alarmHeap = NULL; +} + +static inline AlarmTime +AlarmTime_nowMs(void) { + return SDL_GetTicks(); +} + +Alarm * +Alarm_addAbsoluteMs(uint32 ms, AlarmCallback callback, + AlarmCallbackArg arg) { + Alarm *alarm; + + assert(alarmHeap != NULL); + + alarm = Alarm_alloc(); + alarm->time = ms; + alarm->callback = callback; + alarm->arg = arg; + + Heap_add(alarmHeap, (HeapValue *) alarm); + + return alarm; +} + +Alarm * +Alarm_addRelativeMs(uint32 ms, AlarmCallback callback, + AlarmCallbackArg arg) { + Alarm *alarm; + + assert(alarmHeap != NULL); + + alarm = Alarm_alloc(); + alarm->time = AlarmTime_nowMs() + ms; + alarm->callback = callback; + alarm->arg = arg; + + Heap_add(alarmHeap, (HeapValue *) alarm); + + return alarm; +} + +void +Alarm_remove(Alarm *alarm) { + assert(alarmHeap != NULL); + Heap_remove(alarmHeap, (HeapValue *) alarm); + Alarm_free(alarm); +} + +// Process at most one alarm, if its time has come. +// It is safe to call this function again from inside a callback function +// that it called. It should not be called from multiple threads at once. +bool +Alarm_processOne(void) +{ + AlarmTime now; + Alarm *alarm; + + assert(alarmHeap != NULL); + if (!Heap_hasMore(alarmHeap)) + return false; + + now = AlarmTime_nowMs(); + alarm = (Alarm *) Heap_first(alarmHeap); + if (now < alarm->time) + return false; + + Heap_pop(alarmHeap); + alarm->callback(alarm->arg); + Alarm_free(alarm); + return true; +} + +#if 0 +// It is safe to call this function again from inside a callback function +// that it called. It should not be called from multiple threads at once. +void +Alarm_processAll(void) { + AlarmTime now; + + assert(alarmHeap != NULL); + + now = AlarmTime_nowMs(); + while (Heap_hasMore(alarmHeap)) { + Alarm *alarm = (Alarm *) Heap_first(alarmHeap); + + if (now < alarm->time) + break; + + Heap_pop(alarmHeap); + alarm->callback(alarm->arg); + Alarm_free(alarm); + } +} +#endif + +uint32 +Alarm_timeBeforeNextMs(void) { + Alarm *alarm; + + if (!Heap_hasMore(alarmHeap)) + return UINT32_MAX; + + alarm = (Alarm *) Heap_first(alarmHeap); + return alarmTimeToMsUint32(alarm->time); +} + diff --git a/src/libs/callback/alarm.h b/src/libs/callback/alarm.h new file mode 100644 index 0000000..9f61263 --- /dev/null +++ b/src/libs/callback/alarm.h @@ -0,0 +1,56 @@ +/* + * Copyright 2006 Serge van den Boom <svdb@stack.nl> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LIBS_CALLBACK_ALARM_H_ +#define LIBS_CALLBACK_ALARM_H_ + +#include "port.h" +#include "types.h" + +typedef uint32 AlarmTime; +static inline uint32 +alarmTimeToMsUint32(AlarmTime time) { + return (uint32) time; +} + +typedef struct Alarm Alarm; +typedef void *AlarmCallbackArg; +typedef void (*AlarmCallback)(AlarmCallbackArg arg); + +struct Alarm { + size_t index; + // For the HeapValue 'base struct'. + + AlarmTime time; + AlarmCallback callback; + AlarmCallbackArg arg; +}; + +void Alarm_init(void); +void Alarm_uninit(void); +Alarm *Alarm_addAbsoluteMs(uint32 ms, AlarmCallback callback, + AlarmCallbackArg arg); +Alarm *Alarm_addRelativeMs(uint32 ms, AlarmCallback callback, + AlarmCallbackArg arg); +void Alarm_remove(Alarm *alarm); +bool Alarm_processOne(void); +void Alarm_processAll(void); +uint32 Alarm_timeBeforeNextMs(void); + +#endif /* LIBS_CALLBACK_ALARM_H_ */ + diff --git a/src/libs/callback/async.c b/src/libs/callback/async.c new file mode 100644 index 0000000..d901158 --- /dev/null +++ b/src/libs/callback/async.c @@ -0,0 +1,56 @@ +/* + * Copyright 2012 Serge van den Boom <svdb@stack.nl> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "async.h" + +#include "libs/alarm.h" +#include "libs/callback.h" + + +// Process all alarms and callbacks. +// First, all scheduled callbacks are called. +// Then each alarm due is called, and after each of these alarms, the +// callbacks scheduled by this alarm are called. +void +Async_process(void) +{ + // Call pending callbacks. + Callback_process(); + + for (;;) { + if (!Alarm_processOne()) + return; + + // Call callbacks scheduled from the last alarm. + Callback_process(); + } +} + +// Returns the next time that some asynchronous callback is +// to be called. Note that all values lower than the current time +// should be considered as 'somewhere in the past'. +uint32 +Async_timeBeforeNextMs(void) { + if (Callback_haveMore()) { + // Any time before the current time is ok, though we reserve 0 so + // that the caller may use it as a special value in its own code. + return 1; + } + return Alarm_timeBeforeNextMs(); +} + diff --git a/src/libs/callback/async.h b/src/libs/callback/async.h new file mode 100644 index 0000000..8cfae39 --- /dev/null +++ b/src/libs/callback/async.h @@ -0,0 +1,28 @@ +/* + * Copyright 2012 Serge van den Boom <svdb@stack.nl> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ASYNC_H +#define _ASYNC_H + +#include "types.h" + +void Async_process(void); +uint32 Async_timeBeforeNextMs(void); + +#endif /* _ASYNC_H */ + diff --git a/src/libs/callback/callback.c b/src/libs/callback/callback.c new file mode 100644 index 0000000..e8ae8e9 --- /dev/null +++ b/src/libs/callback/callback.c @@ -0,0 +1,193 @@ +/* + * Copyright 2006 Serge van den Boom <svdb@stack.nl> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "port.h" +#include "types.h" + +#include <assert.h> +#include <stdlib.h> +#include <sys/types.h> + +#include "libs/threadlib.h" + +typedef struct CallbackLink CallbackLink; + +#define CALLBACK_INTERNAL +#include "callback.h" + +struct CallbackLink { + CallbackLink *next; + CallbackFunction callback; + CallbackArg arg; +}; + +static CallbackLink *callbacks; +static CallbackLink **callbacksEnd; +static CallbackLink *const *callbacksProcessEnd; + +static Mutex callbackListLock; + +static inline void +CallbackList_lock(void) { + LockMutex(callbackListLock); +} + +static inline void +CallbackList_unlock(void) { + UnlockMutex(callbackListLock); +} + +#if 0 +static inline bool +CallbackList_isLocked(void) { + // TODO +} +#endif + +void +Callback_init(void) { + callbacks = NULL; + callbacksEnd = &callbacks; + callbacksProcessEnd = &callbacks; + callbackListLock = CreateMutex("Callback List Lock", SYNC_CLASS_TOPLEVEL); +} + +void +Callback_uninit(void) { + // TODO: cleanup the queue? + DestroyMutex (callbackListLock); + callbackListLock = 0; +} + +// Callbacks are guaranteed to be called in the order that they are queued. +CallbackID +Callback_add(CallbackFunction callback, CallbackArg arg) { + CallbackLink *link = malloc(sizeof (CallbackLink)); + link->callback = callback; + link->arg = arg; + link->next = NULL; + + CallbackList_lock(); + *callbacksEnd = link; + callbacksEnd = &link->next; + CallbackList_unlock(); + return (CallbackID) link; +} + + +static void +CallbackLink_delete(CallbackLink *link) { + free(link); +} + +// Pre: CallbackList is locked. +static CallbackLink ** +CallbackLink_find(CallbackLink *link) { + CallbackLink **ptr; + + //assert(CallbackList_isLocked()); + for (ptr = &callbacks; *ptr != NULL; ptr = &(*ptr)->next) { + if (*ptr == link) + return ptr; + } + return NULL; +} + +bool +Callback_remove(CallbackID id) { + CallbackLink *link = (CallbackLink *) id; + CallbackLink **linkPtr; + + CallbackList_lock(); + + linkPtr = CallbackLink_find(link); + if (linkPtr == NULL) { + CallbackList_unlock(); + return false; + } + + if (callbacksEnd == &(*linkPtr)->next) + callbacksEnd = linkPtr; + if (callbacksProcessEnd == &(*linkPtr)->next) + callbacksProcessEnd = linkPtr; + *linkPtr = (*linkPtr)->next; + + CallbackList_unlock(); + + CallbackLink_delete(link); + return true; +} + +static inline void +CallbackLink_doCallback(CallbackLink *link) { + (link->callback)(link->arg); +} + +// Call all queued callbacks currently in the queue. Callbacks queued +// from inside the called functions will not be processed until the next +// call of Callback_process(). +// It is allowed to remove callbacks from inside the called functions. +// NB: Callback_process() must never be called from more than one thread +// at the same time. It's the only sensible way to ensure that the +// callbacks are called in the order in which they were queued. +// It is however allowed to call Callback_process() from inside the +// callback function called by Callback_process() itself. +void +Callback_process(void) { + CallbackLink *link; + + // We set 'callbacksProcessEnd' to callbacksEnd. Callbacks added + // from inside a callback function will be placed after + // callbacksProcessEnd, and will hence not be processed this + // call of Callback_process(). + CallbackList_lock(); + callbacksProcessEnd = callbacksEnd; + CallbackList_unlock(); + + for (;;) { + CallbackList_lock(); + if (callbacksProcessEnd == &callbacks) { + CallbackList_unlock(); + break; + } + assert(callbacks != NULL); + // If callbacks == NULL, then callbacksProcessEnd == &callbacks + link = callbacks; + callbacks = link->next; + if (callbacksEnd == &link->next) + callbacksEnd = &callbacks; + if (callbacksProcessEnd == &link->next) + callbacksProcessEnd = &callbacks; + CallbackList_unlock(); + + CallbackLink_doCallback(link); + CallbackLink_delete(link); + } +} + +bool +Callback_haveMore(void) { + bool result; + + CallbackList_lock(); + result = (callbacks != NULL); + CallbackList_unlock(); + + return result; +} + diff --git a/src/libs/callback/callback.h b/src/libs/callback/callback.h new file mode 100644 index 0000000..e04ebe8 --- /dev/null +++ b/src/libs/callback/callback.h @@ -0,0 +1,43 @@ +/* + * Copyright 2006 Serge van den Boom <svdb@stack.nl> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LIBS_CALLBACK_CALLBACK_H_ +#define LIBS_CALLBACK_CALLBACK_H_ + +#include "types.h" + +#ifdef CALLBACK_INTERNAL +typedef CallbackLink *CallbackID; +#else +typedef void *CallbackID; + // Uniquely identifies a queued callback. +#endif +#define CallbackID_invalid ((CallbackID ) NULL) + +typedef void *CallbackArg; +typedef void (*CallbackFunction)(CallbackArg arg); + +void Callback_init(void); +void Callback_uninit(void); +CallbackID Callback_add(CallbackFunction callback, CallbackArg arg); +bool Callback_remove(CallbackID id); +void Callback_process(void); +bool Callback_haveMore(void); + +#endif /* LIBS_CALLBACK_CALLBACK_H_ */ + |