From dbdd24ae58332fe7579dc2c5abd58b1bd64a6266 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Mon, 25 Jun 2007 17:32:04 +0000 Subject: 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 --- gtk/gtk_schedule.c | 134 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 39 deletions(-) (limited to 'gtk/gtk_schedule.c') 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 + * Copyright 2006-2007 Daniel Silverstone */ #include #include +#include #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); + } +} -- cgit v1.2.3