summaryrefslogtreecommitdiff
path: root/gtk/gtk_schedule.c
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@netsurf-browser.org>2007-06-25 17:32:04 +0000
committerDaniel Silverstone <dsilvers@netsurf-browser.org>2007-06-25 17:32:04 +0000
commitdbdd24ae58332fe7579dc2c5abd58b1bd64a6266 (patch)
treeeff2f15508320245c44fc0bc03ff7a50c6e44703 /gtk/gtk_schedule.c
parent1a677937844ad99c49ffd2fe7e996083dcaed9f7 (diff)
downloadnetsurf-dbdd24ae58332fe7579dc2c5abd58b1bd64a6266.tar.gz
netsurf-dbdd24ae58332fe7579dc2c5abd58b1bd64a6266.tar.bz2
Make the gtk_schedule stuff more robust, only run schedules inside gui_poll and generally cause less issues for the as-yet non-reentrant core.
svn path=/trunk/netsurf/; revision=3366
Diffstat (limited to 'gtk/gtk_schedule.c')
-rw-r--r--gtk/gtk_schedule.c134
1 files changed, 95 insertions, 39 deletions
diff --git a/gtk/gtk_schedule.c b/gtk/gtk_schedule.c
index 13eb54d7d..194b58fd1 100644
--- a/gtk/gtk_schedule.c
+++ b/gtk/gtk_schedule.c
@@ -2,64 +2,120 @@
* This file is part of NetSurf, http://netsurf-browser.org/
* Licensed under the GNU General Public License,
* http://www.opensource.org/licenses/gpl-license
- * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ * Copyright 2006-2007 Daniel Silverstone <dsilvers@digital-scurf.org>
*/
#include <glib.h>
#include <stdlib.h>
+#include <stdbool.h>
#include "desktop/browser.h"
+#ifdef DEBUG_GTK_SCHEDULE
+#include "utils/log.h"
+#else
+#define LOG(X)
+#endif
+
+/** Killable callback closure embodiment. */
typedef struct {
- void (*callback)(void *);
- void *p;
- int die;
-} _nsgtkcallback;
+ void (*callback)(void *); /**< The callback function. */
+ void *context; /**< The context for the callback. */
+ bool callback_killed; /**< Whether or not this was killed. */
+ bool callback_fired; /**< Whether or not this has fired yet. */
+} _nsgtk_callback_t;
-static GList *callbacks;
+/** List of callbacks which have occurred and are pending running. */
+static GList *pending_callbacks = NULL;
+/** List of callbacks which are queued to occur in the future. */
+static GList *queued_callbacks = NULL;
+/** List of callbacks which are about to be run in this ::schedule_run. */
+static GList *this_run = NULL;
-static gboolean ns_generic_gtk_callback(gpointer data)
+static gboolean
+nsgtk_schedule_generic_callback(gpointer data)
{
- _nsgtkcallback *cb = (_nsgtkcallback*)(data);
- if(cb->die) {
- /* We got removed before we got fired off */
- free(cb);
- return FALSE;
- }
- cb->callback(cb->p);
- callbacks = g_list_remove(callbacks, cb);
- free(cb);
- return FALSE;
+ _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(data);
+ if (cb->callback_killed) {
+ /* This callback instance has been killed. */
+ LOG(("CB at %p already dead.", cb));
+ free(cb);
+ return FALSE;
+ }
+ LOG(("CB for %p(%p) set pending.", cb->callback, cb->context));
+ /* The callback is alive, so move it to pending. */
+ cb->callback_fired = true;
+ queued_callbacks = g_list_remove(queued_callbacks, cb);
+ pending_callbacks = g_list_append(pending_callbacks, cb);
+ return FALSE;
}
-void schedule_remove(void (*callback)(void *p), void *p)
+static void
+nsgtk_schedule_kill_callback(void *_target, void *_match)
{
- _nsgtkcallback *cb;
- GList *l;
- l = callbacks;
- while(l) {
- cb = (_nsgtkcallback*)(l->data);
- if(cb->callback == callback && cb->p == p) {
- l = callbacks = g_list_remove(callbacks, cb);
- cb->die = 1;
- } else
- l = g_list_next(l);
- }
+ _nsgtk_callback_t *target = (_nsgtk_callback_t *)_target;
+ _nsgtk_callback_t *match = (_nsgtk_callback_t *)_match;
+ if ((target->callback == match->callback) &&
+ (target->context == match->context)) {
+ LOG(("Found match for %p(%p), killing.",
+ target->callback, target->context));
+ target->callback = NULL;
+ target->context = NULL;
+ target->callback_killed = true;
+ }
}
-void schedule(int t, void (*callback)(void *p), void *p)
+void
+schedule_remove(void (*callback)(void *p), void *p)
{
- _nsgtkcallback *cb = (_nsgtkcallback*)malloc(sizeof(_nsgtkcallback));
- schedule_remove(callback, p);
- cb->callback = callback;
- cb->p = p;
- cb->die = 0;
- callbacks = g_list_prepend(callbacks, cb);
- g_timeout_add(t * 10, ns_generic_gtk_callback, cb);
+ _nsgtk_callback_t cb_match = {
+ .callback = callback,
+ .context = p,
+ };
+
+ g_list_foreach(queued_callbacks,
+ nsgtk_schedule_kill_callback, &cb_match);
+ g_list_foreach(pending_callbacks,
+ nsgtk_schedule_kill_callback, &cb_match);
+ g_list_foreach(this_run,
+ nsgtk_schedule_kill_callback, &cb_match);
}
-void schedule_run(void)
+void
+schedule(int t, void (*callback)(void *p), void *p)
{
- /* Nothing to do, the running is done via the gtk mainloop of joy */
+ const int msec_timeout = t * 100;
+ _nsgtk_callback_t *cb = malloc(sizeof(_nsgtk_callback_t));
+ /* Kill any pending schedule of this kind. */
+ schedule_remove(callback, p);
+ cb->callback = callback;
+ cb->context = p;
+ cb->callback_killed = cb->callback_fired = false;
+ /* Prepend is faster right now. */
+ queued_callbacks = g_list_prepend(queued_callbacks, cb);
+ g_timeout_add(msec_timeout, nsgtk_schedule_generic_callback, cb);
}
+void
+schedule_run(void)
+{
+ /* Capture this run of pending callbacks into the list. */
+ this_run = pending_callbacks;
+
+ if (this_run == NULL)
+ return; /* Nothing to do */
+
+ /* Clear the pending list. */
+ pending_callbacks = NULL;
+
+ LOG(("Captured a run of %d callbacks to fire.", g_list_length(this_run)));
+
+ /* Run all the callbacks which made it this far. */
+ while (this_run != NULL) {
+ _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(this_run->data);
+ this_run = g_list_remove(this_run, this_run->data);
+ if (!cb->callback_killed)
+ cb->callback(cb->context);
+ free(cb);
+ }
+}