Fix Callback_process() being called from a callback called from

Callback_process()


git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2546 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
meep-eep
2006-11-26 03:34:57 +00:00
parent f7e655afd6
commit 66bf4730f8
+33 -23
View File
@@ -16,11 +16,12 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <sys/types.h>
//#include <stdbool.h>
#include "types.h"
#include <stdlib.h>
#include "port.h" #include "port.h"
#include "types.h"
#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
typedef struct CallbackLink CallbackLink; typedef struct CallbackLink CallbackLink;
@@ -34,9 +35,8 @@ struct CallbackLink {
}; };
static CallbackLink *callbacks; static CallbackLink *callbacks;
static CallbackLink *oldCallbacks;
// callback list currently being processed
static CallbackLink **callbacksEnd; static CallbackLink **callbacksEnd;
static CallbackLink *const *callbacksProcessEnd;
static inline void static inline void
CallbackList_lock(void) { CallbackList_lock(void) {
@@ -61,6 +61,7 @@ void
Callback_init(void) { Callback_init(void) {
callbacks = NULL; callbacks = NULL;
callbacksEnd = &callbacks; callbacksEnd = &callbacks;
callbacksProcessEnd = &callbacks;
} }
// Callbacks are guaranteed to be called in the order that they are queued. // Callbacks are guaranteed to be called in the order that they are queued.
@@ -79,12 +80,6 @@ Callback_add(CallbackFunction callback, CallbackArg arg) {
} }
// Pre: CallbackList is locked.
static void
CallbackLink_unlink(CallbackLink **linkPtr) {
*linkPtr = (*linkPtr)->next;
}
static void static void
CallbackLink_delete(CallbackLink *link) { CallbackLink_delete(CallbackLink *link) {
free(link); free(link);
@@ -116,10 +111,15 @@ Callback_removeCallback(CallbackID id) {
return false; return false;
} }
CallbackLink_unlink(linkPtr); if (callbacksEnd == &(*linkPtr)->next)
CallbackLink_delete(link); callbacksEnd = linkPtr;
if (callbacksProcessEnd == &(*linkPtr)->next)
callbacksProcessEnd = linkPtr;
*linkPtr = (*linkPtr)->next;
CallbackList_unlock(); CallbackList_unlock();
CallbackLink_delete(link);
return true; return true;
} }
@@ -131,7 +131,7 @@ CallbackLink_doCallback(CallbackLink *link) {
// Call all queued callbacks currently in the queue. Callbacks queued // Call all queued callbacks currently in the queue. Callbacks queued
// from inside the called functions will not be processed until the next // from inside the called functions will not be processed until the next
// call of Callback_process(). // call of Callback_process().
// It is not allowed to remove callbacks from inside the called functions. // It is allowed to remove callbacks from inside the called functions.
// NB: Callback_process() must never be called from more than one thread // 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 // 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. // callbacks are called in the order in which they were queued.
@@ -141,19 +141,29 @@ void
Callback_process(void) { Callback_process(void) {
CallbackLink *link; CallbackLink *link;
// Create a new queue so that the old queue can be processed // We set 'callbacksProcessEnd' to callbacksEnd. Callbacks added
// at once, without additional locking. // from inside a callback function will be placed after
// callbacksProcessEnd, and will hence not be processed this
// call of Callback_process().
CallbackList_lock(); CallbackList_lock();
oldCallbacks = callbacks; callbacksProcessEnd = callbacksEnd;
callbacks = NULL;
callbacksEnd = &callbacks;
CallbackList_unlock(); CallbackList_unlock();
for (;;) { for (;;) {
link = oldCallbacks; CallbackList_lock();
if (link == NULL) if (callbacksProcessEnd == &callbacks) {
CallbackList_unlock();
break; break;
oldCallbacks = link->next; }
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_doCallback(link);
CallbackLink_delete(link); CallbackLink_delete(link);