Made the SDL event queue dynamically allocated so we don't ever drop events.
authorSam Lantinga <slouken@libsdl.org>
Mon, 10 Jun 2013 23:24:02 -0700
changeset 7304 9598cbf46957
parent 7303 70f0438bf50d
child 7305 d918643aee17
Made the SDL event queue dynamically allocated so we don't ever drop events.
src/events/SDL_events.c
--- a/src/events/SDL_events.c	Sun Jun 09 11:58:31 2013 +0200
+++ b/src/events/SDL_events.c	Mon Jun 10 23:24:02 2013 -0700
@@ -53,17 +53,30 @@
 static Uint32 SDL_userevents = SDL_USEREVENT;
 
 /* Private data -- event queue */
-#define MAXEVENTS   128
+typedef struct _SDL_EventEntry
+{
+    SDL_Event event;
+    SDL_SysWMmsg msg;
+    struct _SDL_EventEntry *prev;
+    struct _SDL_EventEntry *next;
+} SDL_EventEntry;
+
+typedef struct _SDL_SysWMEntry
+{
+    SDL_SysWMmsg msg;
+    struct _SDL_SysWMEntry *next;
+} SDL_SysWMEntry;
+
 static struct
 {
     SDL_mutex *lock;
-    int active;
-    int head;
-    int tail;
-    SDL_Event event[MAXEVENTS];
-    int wmmsg_next;
-    struct SDL_SysWMmsg wmmsg[MAXEVENTS];
-} SDL_EventQ = { NULL, 1 };
+    volatile SDL_bool active;
+    SDL_EventEntry *head;
+    SDL_EventEntry *tail;
+    SDL_EventEntry *free;
+    SDL_SysWMEntry *wmmsg_used;
+    SDL_SysWMEntry *wmmsg_free;
+} SDL_EventQ = { NULL, SDL_TRUE };
 
 
 static __inline__ SDL_bool
@@ -85,18 +98,41 @@
 SDL_StopEventLoop(void)
 {
     int i;
-
-    SDL_EventQ.active = 0;
+    SDL_EventEntry *entry;
+    SDL_SysWMEntry *wmmsg;
 
     if (SDL_EventQ.lock) {
-        SDL_DestroyMutex(SDL_EventQ.lock);
-        SDL_EventQ.lock = NULL;
+        SDL_LockMutex(SDL_EventQ.lock);
     }
 
+    SDL_EventQ.active = SDL_FALSE;
+
     /* Clean out EventQ */
-    SDL_EventQ.head = 0;
-    SDL_EventQ.tail = 0;
-    SDL_EventQ.wmmsg_next = 0;
+    for (entry = SDL_EventQ.head; entry; ) {
+        SDL_EventEntry *next = entry->next;
+        SDL_free(entry);
+        entry = next;
+    }
+    for (entry = SDL_EventQ.free; entry; ) {
+        SDL_EventEntry *next = entry->next;
+        SDL_free(entry);
+        entry = next;
+    }
+    for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; ) {
+        SDL_SysWMEntry *next = wmmsg->next;
+        SDL_free(wmmsg);
+        wmmsg = next;
+    }
+    for (wmmsg = SDL_EventQ.wmmsg_free; wmmsg; ) {
+        SDL_SysWMEntry *next = wmmsg->next;
+        SDL_free(wmmsg);
+        wmmsg = next;
+    }
+    SDL_EventQ.head = NULL;
+    SDL_EventQ.tail = NULL;
+    SDL_EventQ.free = NULL;
+    SDL_EventQ.wmmsg_used = NULL;
+    SDL_EventQ.wmmsg_free = NULL;
 
     /* Clear disabled event state */
     for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
@@ -112,6 +148,12 @@
         SDL_free(tmp);
     }
     SDL_EventOK = NULL;
+
+    if (SDL_EventQ.lock) {
+        SDL_UnlockMutex(SDL_EventQ.lock);
+        SDL_DestroyMutex(SDL_EventQ.lock);
+        SDL_EventQ.lock = NULL;
+    }
 }
 
 /* This function (and associated calls) may be called more than once */
@@ -139,7 +181,7 @@
     SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
 
-    SDL_EventQ.active = 1;
+    SDL_EventQ.active = SDL_TRUE;
 
     return (0);
 }
@@ -149,55 +191,61 @@
 static int
 SDL_AddEvent(SDL_Event * event)
 {
-    int tail, added;
+    SDL_EventEntry *entry;
 
-    tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
-    if (tail == SDL_EventQ.head) {
-        /* Overflow, drop event */
-        added = 0;
+    if (SDL_EventQ.free == NULL) {
+        entry = (SDL_EventEntry *)SDL_malloc(sizeof(*entry));
+        if (!entry) {
+            return 0;
+        }
     } else {
-        SDL_EventQ.event[SDL_EventQ.tail] = *event;
-        if (event->type == SDL_SYSWMEVENT) {
-            /* Note that it's possible to lose an event */
-            int next = SDL_EventQ.wmmsg_next;
-            SDL_EventQ.wmmsg[next] = *event->syswm.msg;
-            SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
-                &SDL_EventQ.wmmsg[next];
-            SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
-        }
-        SDL_EventQ.tail = tail;
-        added = 1;
+        entry = SDL_EventQ.free;
+        SDL_EventQ.free = entry->next;
+    }
+
+    entry->event = *event;
+    if (event->type == SDL_SYSWMEVENT) {
+        entry->msg = *event->syswm.msg;
     }
-    return (added);
+
+    if (SDL_EventQ.tail) {
+        SDL_EventQ.tail->next = entry;
+        entry->prev = SDL_EventQ.tail;
+        SDL_EventQ.tail = entry;
+        entry->next = NULL;
+    } else {
+        SDL_assert(!SDL_EventQ.head);
+        SDL_EventQ.head = entry;
+        SDL_EventQ.tail = entry;
+        entry->prev = NULL;
+        entry->next = NULL;
+    }
+
+    return 1;
 }
 
-/* Cut an event, and return the next valid spot, or the tail */
-/*                           -- called with the queue locked */
-static int
-SDL_CutEvent(int spot)
+/* Remove an event from the queue -- called with the queue locked */
+static void
+SDL_CutEvent(SDL_EventEntry *entry)
 {
-    if (spot == SDL_EventQ.head) {
-        SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
-        return (SDL_EventQ.head);
-    } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
-        SDL_EventQ.tail = spot;
-        return (SDL_EventQ.tail);
-    } else
-        /* We cut the middle -- shift everything over */
-    {
-        int here, next;
+    if (entry->prev) {
+        entry->prev->next = entry->next;
+    }
+    if (entry->next) {
+        entry->next->prev = entry->prev;
+    }
 
-        /* This can probably be optimized with SDL_memcpy() -- careful! */
-        if (--SDL_EventQ.tail < 0) {
-            SDL_EventQ.tail = MAXEVENTS - 1;
-        }
-        for (here = spot; here != SDL_EventQ.tail; here = next) {
-            next = (here + 1) % MAXEVENTS;
-            SDL_EventQ.event[here] = SDL_EventQ.event[next];
-        }
-        return (spot);
+    if (entry == SDL_EventQ.head) {
+        SDL_assert(entry->prev == NULL);
+        SDL_EventQ.head = entry->next;
     }
-    /* NOTREACHED */
+    if (entry == SDL_EventQ.tail) {
+        SDL_assert(entry->next == NULL);
+        SDL_EventQ.tail = entry->prev;
+    }
+
+    entry->next = SDL_EventQ.free;
+    SDL_EventQ.free = entry;
 }
 
 /* Lock the event queue, take a peep at it, and unlock it */
@@ -209,6 +257,10 @@
 
     /* Don't look after we've quit */
     if (!SDL_EventQ.active) {
+        /* We get a few spurious events at shutdown, so don't warn then */
+        if (action != SDL_ADDEVENT) {
+            SDL_SetError("The event system has been shut down");
+        }
         return (-1);
     }
     /* Lock the event queue */
@@ -219,8 +271,10 @@
                 used += SDL_AddEvent(&events[i]);
             }
         } else {
+            SDL_EventEntry *entry, *next;
+            SDL_SysWMEntry *wmmsg, *wmmsg_next;
             SDL_Event tmpevent;
-            int spot;
+            Uint32 type;
 
             /* If 'events' is NULL, just see if they exist */
             if (events == NULL) {
@@ -228,18 +282,44 @@
                 numevents = 1;
                 events = &tmpevent;
             }
-            spot = SDL_EventQ.head;
-            while ((used < numevents) && (spot != SDL_EventQ.tail)) {
-                Uint32 type = SDL_EventQ.event[spot].type;
+
+            /* Clean out any used wmmsg data
+               FIXME: Do we want to retain the data for some period of time?
+             */
+            for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; wmmsg = wmmsg_next) {
+                wmmsg_next = wmmsg->next;
+                wmmsg->next = SDL_EventQ.wmmsg_free;
+                SDL_EventQ.wmmsg_free = wmmsg;
+            }
+            SDL_EventQ.wmmsg_used = NULL;
+
+            for (entry = SDL_EventQ.head; entry && used < numevents; entry = next) {
+                next = entry->next;
+                type = entry->event.type;
                 if (minType <= type && type <= maxType) {
-                    events[used++] = SDL_EventQ.event[spot];
+                    events[used] = entry->event;
+                    if (entry->event.type == SDL_SYSWMEVENT) {
+                        /* We need to copy the wmmsg somewhere safe.
+                           For now we'll guarantee it's valid at least until
+                           the next call to SDL_PeepEvents()
+                         */
+                        SDL_SysWMEntry *wmmsg;
+                        if (SDL_EventQ.wmmsg_free) {
+                            wmmsg = SDL_EventQ.wmmsg_free;
+                            SDL_EventQ.wmmsg_free = wmmsg->next;
+                        } else {
+                            wmmsg = (SDL_SysWMEntry *)SDL_malloc(sizeof(*wmmsg));
+                        }
+                        wmmsg->msg = *entry->event.syswm.msg;
+                        wmmsg->next = SDL_EventQ.wmmsg_used;
+                        SDL_EventQ.wmmsg_used = wmmsg;
+                        events[used].syswm.msg = &wmmsg->msg;
+                    }
+                    ++used;
+
                     if (action == SDL_GETEVENT) {
-                        spot = SDL_CutEvent(spot);
-                    } else {
-                        spot = (spot + 1) % MAXEVENTS;
+                        SDL_CutEvent(entry);
                     }
-                } else {
-                    spot = (spot + 1) % MAXEVENTS;
                 }
             }
         }
@@ -286,13 +366,13 @@
 
     /* Lock the event queue */
     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
-        int spot = SDL_EventQ.head;
-        while (spot != SDL_EventQ.tail) {
-            Uint32 type = SDL_EventQ.event[spot].type;
+        SDL_EventEntry *entry, *next;
+        Uint32 type;
+        for (entry = SDL_EventQ.head; entry; entry = next) {
+            next = entry->next;
+            type = entry->event.type;
             if (minType <= type && type <= maxType) {
-                spot = SDL_CutEvent(spot);
-            } else {
-                spot = (spot + 1) % MAXEVENTS;
+                SDL_CutEvent(entry);
             }
         }
         SDL_UnlockMutex(SDL_EventQ.lock);
@@ -448,18 +528,15 @@
 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
 {
     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
-        int spot;
-
-        spot = SDL_EventQ.head;
-        while (spot != SDL_EventQ.tail) {
-            if (filter(userdata, &SDL_EventQ.event[spot])) {
-                spot = (spot + 1) % MAXEVENTS;
-            } else {
-                spot = SDL_CutEvent(spot);
+        SDL_EventEntry *entry, *next;
+        for (entry = SDL_EventQ.head; entry; entry = next) {
+            next = entry->next;
+            if (!filter(userdata, &entry->event)) {
+                SDL_CutEvent(entry);
             }
         }
+        SDL_UnlockMutex(SDL_EventQ.lock);
     }
-    SDL_UnlockMutex(SDL_EventQ.lock);
 }
 
 Uint8