summaryrefslogtreecommitdiff
path: root/src/libs/callback/callback.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/callback/callback.c')
-rw-r--r--src/libs/callback/callback.c193
1 files changed, 193 insertions, 0 deletions
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;
+}
+