summaryrefslogtreecommitdiff
path: root/src/libs/callback
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/callback')
-rw-r--r--src/libs/callback/Makeinfo2
-rw-r--r--src/libs/callback/alarm.c177
-rw-r--r--src/libs/callback/alarm.h56
-rw-r--r--src/libs/callback/async.c56
-rw-r--r--src/libs/callback/async.h28
-rw-r--r--src/libs/callback/callback.c193
-rw-r--r--src/libs/callback/callback.h43
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_ */
+