changeset 5113:481dabb098ef

Improved timer implementation The new timer model is formalized as using a separate thread to handle timer callbacks. This was the case on almost every platform before, but it's now a requirement, and simplifies the implementation and makes it perform consistently across platforms. Goals: * Minimize timer thread blocking * Dispatch timers as accurately as possible * SDL_AddTimer() and SDL_RemoveTimer() are completely threadsafe * SDL_RemoveTimer() doesn't crash with a timer that's expired or removed
author Sam Lantinga <slouken@libsdl.org>
date Thu, 27 Jan 2011 14:45:06 -0800
parents 0846f18eb625
children 39772ccf7ddd
files include/SDL_compat.h include/SDL_timer.h src/SDL_compat.c src/events/SDL_events.c src/timer/SDL_systimer.h src/timer/SDL_timer.c src/timer/SDL_timer_c.h src/timer/beos/SDL_systimer.c src/timer/dummy/SDL_systimer.c src/timer/nds/SDL_systimer.c src/timer/unix/SDL_systimer.c src/timer/wince/SDL_systimer.c src/timer/windows/SDL_systimer.c
diffstat 13 files changed, 378 insertions(+), 832 deletions(-) [+]
line wrap: on
line diff
--- a/include/SDL_compat.h	Thu Jan 27 10:40:17 2011 -0800
+++ b/include/SDL_compat.h	Thu Jan 27 14:45:06 2011 -0800
@@ -344,6 +344,13 @@
 #define SDL_RenderFill(X)  (X) ? SDL_RenderFillRect(X) : SDL_RenderClear()
 #define SDL_KillThread(X)
 
+/* The timeslice and timer resolution are no longer relevant */
+#define SDL_TIMESLICE		10
+#define TIMER_RESOLUTION	10
+
+typedef Uint32 (SDLCALL * SDL_OldTimerCallback) (Uint32 interval);
+extern DECLSPEC int SDLCALL SDL_SetTimer(Uint32 interval, SDL_OldTimerCallback callback);
+
 extern DECLSPEC int SDLCALL SDL_putenv(const char *variable);
 
 /*@}*//*Compatibility*/
--- a/include/SDL_timer.h	Thu Jan 27 10:40:17 2011 -0800
+++ b/include/SDL_timer.h	Thu Jan 27 14:45:06 2011 -0800
@@ -41,104 +41,50 @@
 #endif
 
 /**
- *  This is the OS scheduler timeslice, in milliseconds.
- */
-#define SDL_TIMESLICE		10
-
-/**
- *  This is the maximum resolution of the SDL timer on all platforms.
- */
-#define TIMER_RESOLUTION	10      /**< Experimentally determined */
-
-/**
- *  Get the number of milliseconds since the SDL library initialization.
+ * \brief Get the number of milliseconds since the SDL library initialization.
  *  
- *  Note that this value wraps if the program runs for more than ~49 days.
+ * \note This value wraps if the program runs for more than ~49 days.
  */
 extern DECLSPEC Uint32 SDLCALL SDL_GetTicks(void);
 
 /**
- *  Wait a specified number of milliseconds before returning.
+ * \brief Wait a specified number of milliseconds before returning.
  */
 extern DECLSPEC void SDLCALL SDL_Delay(Uint32 ms);
 
 /**
  *  Function prototype for the timer callback function.
- */
-typedef Uint32(SDLCALL * SDL_TimerCallback) (Uint32 interval);
-
-/**
- *  Set a callback to run after the specified number of milliseconds has
- *  elapsed. The callback function is passed the current timer interval
- *  and returns the next timer interval.  If the returned value is the 
- *  same as the one passed in, the periodic alarm continues, otherwise a
- *  new alarm is scheduled.  If the callback returns 0, the periodic alarm
- *  is cancelled.
- *  
- *  To cancel a currently running timer, call 
- *  \code SDL_SetTimer(0, NULL); \endcode
- *  
- *  The timer callback function may run in a different thread than your
- *  main code, and so shouldn't call any functions from within itself.
- *  
- *  The maximum resolution of this timer is 10 ms, which means that if
- *  you request a 16 ms timer, your callback will run approximately 20 ms
- *  later on an unloaded system.  If you wanted to set a flag signaling
- *  a frame update at 30 frames per second (every 33 ms), you might set a 
- *  timer for 30 ms:
- *  \code
- *    SDL_SetTimer((33/10)*10, flag_update);
- *  \endcode
- *  
- *  If you use this function, you need to pass ::SDL_INIT_TIMER to SDL_Init().
- *  
- *  Under UNIX, you should not use raise or use SIGALRM and this function
- *  in the same program, as it is implemented using setitimer().  You also
- *  should not use this function in multi-threaded applications as signals
- *  to multi-threaded apps have undefined behavior in some implementations.
- *  
- *  \return 0 if successful, or -1 if there was an error.
- */
-extern DECLSPEC int SDLCALL SDL_SetTimer(Uint32 interval,
-                                         SDL_TimerCallback callback);
-
-/**
- *  \name Peter timers
- *  New timer API, supports multiple timers
- *  Written by Stephane Peter <megastep@lokigames.com>
- */
-/*@{*/
-
-/**
- *  Function prototype for the new timer callback function.
  *  
  *  The callback function is passed the current timer interval and returns
  *  the next timer interval.  If the returned value is the same as the one
  *  passed in, the periodic alarm continues, otherwise a new alarm is
  *  scheduled.  If the callback returns 0, the periodic alarm is cancelled.
  */
-typedef Uint32(SDLCALL * SDL_NewTimerCallback) (Uint32 interval, void *param);
+typedef Uint32 (SDLCALL * SDL_TimerCallback) (Uint32 interval, void *param);
 
 /**
- *  Definition of the timer ID type.
+ * Definition of the timer ID type.
  */
-typedef struct _SDL_TimerID *SDL_TimerID;
+typedef int SDL_TimerID;
 
 /**
- *  Add a new timer to the pool of timers already running.
- *  \return A timer ID, or NULL when an error occurs.
+ * \brief Add a new timer to the pool of timers already running.
+ *
+ * \return A timer ID, or NULL when an error occurs.
  */
 extern DECLSPEC SDL_TimerID SDLCALL SDL_AddTimer(Uint32 interval,
-                                                 SDL_NewTimerCallback
-                                                 callback, void *param);
+                                                 SDL_TimerCallback callback,
+                                                 void *param);
 
 /**
- *  Remove one of the multiple timers knowing its ID.
- *  \return A boolean value indicating success or failure.
+ * \brief Remove a timer knowing its ID.
+ *
+ * \return A boolean value indicating success or failure.
+ *
+ * \warning It is not safe to remove a timer multiple times.
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_RemoveTimer(SDL_TimerID t);
 
-/*@}*//*Peter timers*/
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
--- a/src/SDL_compat.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/SDL_compat.c	Thu Jan 27 14:45:06 2011 -0800
@@ -1760,6 +1760,31 @@
     return previous;
 }
 
+static Uint32
+SDL_SetTimerCallback(Uint32 interval, void* param)
+{
+    return ((SDL_OldTimerCallback)param)(interval);
+}
+
+int
+SDL_SetTimer(Uint32 interval, SDL_OldTimerCallback callback)
+{
+    static SDL_TimerID compat_timer;
+
+    if (compat_timer) {
+        SDL_RemoveTimer(compat_timer);
+        compat_timer = 0;
+    }
+
+    if (interval && callback) {
+        compat_timer = SDL_AddTimer(interval, SDL_SetTimerCallback, callback);
+        if (!compat_timer) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
 int
 SDL_putenv(const char *_var)
 {
--- a/src/events/SDL_events.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/events/SDL_events.c	Thu Jan 27 14:45:06 2011 -0800
@@ -123,9 +123,6 @@
 
         /* Give up the CPU for the rest of our timeslice */
         SDL_EventLock.safe = 1;
-        if (SDL_timer_running) {
-            SDL_ThreadedTimerCheck();
-        }
         SDL_Delay(1);
 
         /* Check for event locking.
@@ -140,7 +137,6 @@
         SDL_EventLock.safe = 0;
         SDL_mutexV(SDL_EventLock.lock);
     }
-    SDL_SetTimerThreaded(0);
     event_thread = 0;
     return (0);
 }
@@ -168,8 +164,6 @@
         }
         SDL_EventLock.safe = 0;
 
-        /* The event thread will handle timers too */
-        SDL_SetTimerThreaded(2);
 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
 #undef SDL_CreateThread
         SDL_EventThread =
--- a/src/timer/SDL_systimer.h	Thu Jan 27 10:40:17 2011 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
-    SDL - Simple DirectMedia Layer
-    Copyright (C) 1997-2010 Sam Lantinga
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library 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
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-    Sam Lantinga
-    slouken@libsdl.org
-*/
-#include "SDL_config.h"
-
-/* The system dependent timer handling functions */
-
-#include "SDL_timer.h"
-#include "SDL_timer_c.h"
-
-
-/* Initialize the system dependent timer subsystem */
-extern int SDL_SYS_TimerInit(void);
-
-/* Quit the system dependent timer subsystem */
-extern void SDL_SYS_TimerQuit(void);
-
-/* Start a timer set up by SDL_SetTimer() */
-extern int SDL_SYS_StartTimer(void);
-
-/* Stop a previously started timer */
-extern void SDL_SYS_StopTimer(void);
-
-/* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/SDL_timer.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/SDL_timer.c	Thu Jan 27 14:45:06 2011 -0800
@@ -23,289 +23,360 @@
 
 #include "SDL_timer.h"
 #include "SDL_timer_c.h"
-#include "SDL_mutex.h"
-#include "SDL_systimer.h"
+#include "SDL_atomic.h"
+#include "SDL_thread.h"
 
 /* #define DEBUG_TIMERS */
 
-int SDL_timer_started = 0;
-int SDL_timer_running = 0;
+typedef struct _SDL_Timer
+{
+    int timerID;
+    SDL_TimerCallback callback;
+    void *param;
+    Uint32 interval;
+    Uint32 scheduled;
+    volatile SDL_bool canceled;
+    struct _SDL_Timer *next;
+} SDL_Timer;
+
+typedef struct _SDL_TimerMap
+{
+    int timerID;
+    SDL_Timer *timer;
+    struct _SDL_TimerMap *next;
+} SDL_TimerMap;
 
-/* Data to handle a single periodic alarm */
-Uint32 SDL_alarm_interval = 0;
-SDL_TimerCallback SDL_alarm_callback;
+/* A reasonable guess */
+#define CACHELINE_SIZE  128
+
+/* The timers are kept in a sorted list */
+typedef struct {
+    /* Data used by the main thread */
+    SDL_Thread *thread;
+    SDL_atomic_t nextID;
+    SDL_TimerMap *timermap;
+    SDL_mutex *timermap_lock;
+
+    /* Padding to separate cache lines between threads */
+    char pad[CACHELINE_SIZE];
+
+    /* Data used to communicate with the timer thread */
+    SDL_SpinLock lock;
+    SDL_sem *sem;
+    SDL_Timer * volatile pending;
+    SDL_Timer * volatile freelist;
+    volatile SDL_bool active;
 
-/* Data used for a thread-based timer */
-static int SDL_timer_threaded = 0;
+    /* List of timers - this is only touched by the timer thread */
+    SDL_Timer *timers;
+} SDL_TimerData;
+
+static SDL_TimerData SDL_timer_data;
+
 
-struct _SDL_TimerID
+/* The idea here is that any thread might add a timer, but a single
+ * thread manages the active timer queue, sorted by scheduling time.
+ *
+ * Timers are removed by simply setting a canceled flag
+ */
+
+static void
+SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
 {
-    Uint32 interval;
-    SDL_NewTimerCallback cb;
-    void *param;
-    Uint32 last_alarm;
-    struct _SDL_TimerID *next;
-};
+    SDL_Timer *prev, *curr;
+
+    prev = NULL;
+    for (curr = data->timers; curr; prev = curr, curr = curr->next) {
+        if ((Sint32)(timer->scheduled-curr->scheduled) < 0) {
+            break;
+        }
+    }
+
+    /* Insert the timer here! */
+    if (prev) {
+        prev->next = timer;
+    } else {
+        data->timers = timer;
+    }
+    timer->next = curr;
+}
+
+static int
+SDL_TimerThread(void *_data)
+{
+    SDL_TimerData *data = (SDL_TimerData *)_data;
+    SDL_Timer *pending;
+    SDL_Timer *current;
+    SDL_Timer *freelist_head = NULL;
+    SDL_Timer *freelist_tail = NULL;
+    Uint32 tick, now, interval, delay;
 
-static SDL_TimerID SDL_timers = NULL;
-static SDL_mutex *SDL_timer_mutex;
-static volatile SDL_bool list_changed = SDL_FALSE;
+    /* Threaded timer loop:
+     *  1. Queue timers added by other threads
+     *  2. Handle any timers that should dispatch this cycle
+     *  3. Wait until next dispatch time or new timer arrives
+     */
+    for ( ; ; ) {
+        /* Pending and freelist maintenance */
+        SDL_AtomicLock(&data->lock);
+        {
+            /* Get any timers ready to be queued */
+            pending = data->pending;
+            data->pending = NULL;
+
+            /* Make any unused timer structures available */
+            if (freelist_head) {
+                freelist_tail->next = data->freelist;
+                data->freelist = freelist_head;
+            }
+        }
+        SDL_AtomicUnlock(&data->lock);
+
+        /* Sort the pending timers into our list */
+        while (pending) {
+            current = pending;
+            pending = pending->next;
+            SDL_AddTimerInternal(data, current);
+        }
+        freelist_head = NULL;
+        freelist_tail = NULL;
+
+        /* Check to see if we're still running, after maintenance */
+        if (!data->active) {
+            break;
+        }
+
+        /* Initial delay if there are no timers */
+        delay = SDL_MUTEX_MAXWAIT;
+
+        tick = SDL_GetTicks();
+
+        /* Process all the pending timers for this tick */
+        while (data->timers) {
+            current = data->timers;
 
-/* Set whether or not the timer should use a thread.
-   This should not be called while the timer subsystem is running.
-*/
-int
-SDL_SetTimerThreaded(int value)
-{
-    int retval;
+            if ((Sint32)(tick-current->scheduled) < 0) {
+                /* Scheduled for the future, wait a bit */
+                delay = (current->scheduled - tick);
+                break;
+            }
+
+            /* We're going to do something with this timer */
+            data->timers = current->next;
+
+            if (current->canceled) {
+                interval = 0;
+            } else {
+                interval = current->callback(current->interval, current->param);
+            }
 
-    if (SDL_timer_started) {
-        SDL_SetError("Timer already initialized");
-        retval = -1;
-    } else {
-        retval = 0;
-        SDL_timer_threaded = value;
+            if (interval > 0) {
+                /* Reschedule this timer */
+                current->scheduled = tick + interval;
+                SDL_AddTimerInternal(data, current);
+            } else {
+                if (!freelist_head) {
+                    freelist_head = current;
+                }
+                if (freelist_tail) {
+                    freelist_tail->next = current;
+                }
+                freelist_tail = current;
+
+                current->canceled = SDL_TRUE;
+            }
+        }
+
+        /* Adjust the delay based on processing time */
+        now = SDL_GetTicks();
+        interval = (now - tick);
+        if (interval > delay) {
+            delay = 0;
+        } else {
+            delay -= interval;
+        }
+
+        /* Note that each time a timer is added, this will return
+           immediately, but we process the timers added all at once.
+           That's okay, it just means we run through the loop a few
+           extra times.
+         */
+        SDL_SemWaitTimeout(data->sem, delay);
     }
-    return retval;
+    return 0;
 }
 
 int
 SDL_TimerInit(void)
 {
-    int retval;
+    SDL_TimerData *data = &SDL_timer_data;
+
+    if (!data->active) {
+        data->timermap_lock = SDL_CreateMutex();
+        if (!data->timermap_lock) {
+            return -1;
+        }
 
-    retval = 0;
-    if (SDL_timer_started) {
-        SDL_TimerQuit();
+        data->sem = SDL_CreateSemaphore(0);
+        if (!data->sem) {
+            SDL_DestroyMutex(data->timermap_lock);
+            return -1;
+        }
+
+        data->active = SDL_TRUE;
+        data->thread = SDL_CreateThread(SDL_TimerThread, data);
+        if (!data->thread) {
+            SDL_TimerQuit();
+            return -1;
+        }
+
+        SDL_AtomicSet(&data->nextID, 1);
     }
-    if (!SDL_timer_threaded) {
-        retval = SDL_SYS_TimerInit();
-    }
-    if (SDL_timer_threaded) {
-        SDL_timer_mutex = SDL_CreateMutex();
-    }
-    if (retval == 0) {
-        SDL_timer_started = 1;
-    }
-    return (retval);
+    return 0;
 }
 
 void
 SDL_TimerQuit(void)
 {
-    SDL_SetTimer(0, NULL);
-    if (SDL_timer_threaded < 2) {
-        SDL_SYS_TimerQuit();
-    }
-    if (SDL_timer_threaded) {
-        SDL_DestroyMutex(SDL_timer_mutex);
-        SDL_timer_mutex = NULL;
-    }
-    SDL_timer_started = 0;
-    SDL_timer_threaded = 0;
-}
+    SDL_TimerData *data = &SDL_timer_data;
+    SDL_Timer *timer;
+    SDL_TimerMap *entry;
+
+    if (data->active) {
+        data->active = SDL_FALSE;
 
-void
-SDL_ThreadedTimerCheck(void)
-{
-    Uint32 now, ms;
-    SDL_TimerID t, prev, next;
-    SDL_bool removed;
-
-    SDL_mutexP(SDL_timer_mutex);
+        /* Shutdown the timer thread */
+        if (data->thread) {
+            SDL_SemPost(data->sem);
+            SDL_WaitThread(data->thread, NULL);
+            data->thread = NULL;
+        }
 
-    now = SDL_GetTicks();
-    do {
-        list_changed = SDL_FALSE;
-        for (prev = NULL, t = SDL_timers; t; t = next) {
-            removed = SDL_FALSE;
-            ms = t->interval - SDL_TIMESLICE;
-            next = t->next;
-            if ((int) (now - t->last_alarm) > (int) ms) {
-                struct _SDL_TimerID timer;
+        SDL_DestroySemaphore(data->sem);
+        data->sem = NULL;
 
-                if ((now - t->last_alarm) < t->interval) {
-                    t->last_alarm += t->interval;
-                } else {
-                    t->last_alarm = now;
-                }
-#ifdef DEBUG_TIMERS
-                printf("Executing timer %p (thread = %lu)\n",
-                       t, SDL_ThreadID());
-#endif
-                timer = *t;
-                SDL_mutexV(SDL_timer_mutex);
-                ms = timer.cb(timer.interval, timer.param);
-                SDL_mutexP(SDL_timer_mutex);
-                if (list_changed) {
-                    next = t->next;
-                    for (prev = SDL_timers; prev; prev = prev->next) {
-                        if (prev->next == t)
-                            break;
-                    }
-                }
-                if (ms != t->interval) {
-                    if (ms) {
-                        t->interval = ROUND_RESOLUTION(ms);
-                    } else {
-                        /* Remove timer from the list */
-#ifdef DEBUG_TIMERS
-                        printf("SDL: Removing timer %p\n", t);
-#endif
-                        if (prev) {
-                            prev->next = next;
-                        } else {
-                            SDL_timers = next;
-                        }
-                        SDL_free(t);
-                        --SDL_timer_running;
-                        removed = SDL_TRUE;
-                    }
-                }
-                if (list_changed) {
-                    /* Abort, list of timers modified */
-                    break;
-                }
-            }
-            /* Don't update prev if the timer has disappeared */
-            if (!removed) {
-                prev = t;
-            }
+        /* Clean up the timer entries */
+        while (data->timers) {
+            timer = data->timers;
+            data->timers = timer->next;
+            SDL_free(timer);
         }
-    } while (list_changed);
-
-    SDL_mutexV(SDL_timer_mutex);
-}
+        while (data->freelist) {
+            timer = data->freelist;
+            data->freelist = timer->next;
+            SDL_free(timer);
+        }
+        while (data->timermap) {
+            entry = data->timermap;
+            data->timermap = entry->next;
+            SDL_free(entry);
+        }
 
-static SDL_TimerID
-SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback,
-                     void *param)
-{
-    SDL_TimerID t;
-    t = (SDL_TimerID) SDL_malloc(sizeof(struct _SDL_TimerID));
-    if (t) {
-        t->interval = ROUND_RESOLUTION(interval);
-        t->cb = callback;
-        t->param = param;
-        t->last_alarm = SDL_GetTicks();
-        t->next = SDL_timers;
-        SDL_timers = t;
-        ++SDL_timer_running;
-        list_changed = SDL_TRUE;
+        SDL_DestroyMutex(data->timermap_lock);
+        data->timermap_lock = NULL;
     }
-#ifdef DEBUG_TIMERS
-    printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32) t,
-           SDL_timer_running);
-#endif
-    return t;
 }
 
 SDL_TimerID
-SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)
+SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
 {
-    SDL_TimerID t;
-    if (!SDL_timer_mutex) {
-        if (SDL_timer_started) {
-            SDL_SetError("This platform doesn't support multiple timers");
-        } else {
-            SDL_SetError("You must call SDL_Init(SDL_INIT_TIMER) first");
+    SDL_TimerData *data = &SDL_timer_data;
+    SDL_Timer *timer;
+    SDL_TimerMap *entry;
+
+    if (!data->active) {
+        int status = 0;
+
+        SDL_AtomicLock(&data->lock);
+        if (!data->active) {
+            status = SDL_TimerInit();
+        }
+        SDL_AtomicUnlock(&data->lock);
+
+        if (status < 0) {
+            return 0;
         }
-        return NULL;
+    }
+
+    SDL_AtomicLock(&data->lock);
+    timer = data->freelist;
+    if (timer) {
+        data->freelist = timer->next;
+    }
+    SDL_AtomicUnlock(&data->lock);
+
+    if (timer) {
+        SDL_RemoveTimer(timer->timerID);
+    } else {
+        timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
+        if (!timer) {
+            SDL_OutOfMemory();
+            return 0;
+        }
     }
-    if (!SDL_timer_threaded) {
-        SDL_SetError("Multiple timers require threaded events!");
-        return NULL;
+    timer->timerID = SDL_AtomicIncRef(&data->nextID);
+    timer->callback = callback;
+    timer->param = param;
+    timer->interval = interval;
+    timer->scheduled = SDL_GetTicks() + interval;
+    timer->canceled = SDL_FALSE;
+ 
+    entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
+    if (!entry) {
+        SDL_free(timer);
+        SDL_OutOfMemory();
+        return 0;
     }
-    SDL_mutexP(SDL_timer_mutex);
-    t = SDL_AddTimerInternal(interval, callback, param);
-    SDL_mutexV(SDL_timer_mutex);
-    return t;
+    entry->timer = timer;
+    entry->timerID = timer->timerID;
+
+    SDL_mutexP(data->timermap_lock);
+    entry->next = data->timermap;
+    data->timermap = entry;
+    SDL_mutexV(data->timermap_lock);
+
+    /* Add the timer to the pending list for the timer thread */
+    SDL_AtomicLock(&data->lock);
+    timer->next = data->pending;
+    data->pending = timer;
+    SDL_AtomicUnlock(&data->lock);
+
+    /* Wake up the timer thread if necessary */
+    SDL_SemPost(data->sem);
+
+    return entry->timerID;
 }
 
 SDL_bool
 SDL_RemoveTimer(SDL_TimerID id)
 {
-    SDL_TimerID t, prev = NULL;
-    SDL_bool removed;
+    SDL_TimerData *data = &SDL_timer_data;
+    SDL_TimerMap *prev, *entry;
+    SDL_bool canceled = SDL_FALSE;
 
-    removed = SDL_FALSE;
-    SDL_mutexP(SDL_timer_mutex);
-    /* Look for id in the linked list of timers */
-    for (t = SDL_timers; t; prev = t, t = t->next) {
-        if (t == id) {
+    /* Find the timer */
+    SDL_mutexP(data->timermap_lock);
+    prev = NULL;
+    for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
+        if (entry->timerID == id) {
             if (prev) {
-                prev->next = t->next;
+                prev->next = entry->next;
             } else {
-                SDL_timers = t->next;
+                data->timermap = entry->next;
             }
-            SDL_free(t);
-            --SDL_timer_running;
-            removed = SDL_TRUE;
-            list_changed = SDL_TRUE;
             break;
         }
     }
-#ifdef DEBUG_TIMERS
-    printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %lu\n",
-           (Uint32) id, removed, SDL_timer_running, SDL_ThreadID());
-#endif
-    SDL_mutexV(SDL_timer_mutex);
-    return removed;
-}
+    SDL_mutexV(data->timermap_lock);
 
-/* Old style callback functions are wrapped through this */
-static Uint32 SDLCALL
-callback_wrapper(Uint32 ms, void *param)
-{
-    SDL_TimerCallback func = (SDL_TimerCallback) param;
-    return (*func) (ms);
-}
-
-int
-SDL_SetTimer(Uint32 ms, SDL_TimerCallback callback)
-{
-    int retval;
-
-#ifdef DEBUG_TIMERS
-    printf("SDL_SetTimer(%d)\n", ms);
-#endif
-    retval = 0;
-
-    if (SDL_timer_threaded) {
-        SDL_mutexP(SDL_timer_mutex);
+    if (entry) {
+        if (!entry->timer->canceled) {
+            entry->timer->canceled = SDL_TRUE;
+            canceled = SDL_TRUE;
+        }
+        SDL_free(entry);
     }
-    if (SDL_timer_running) {    /* Stop any currently running timer */
-        if (SDL_timer_threaded) {
-            while (SDL_timers) {
-                SDL_TimerID freeme = SDL_timers;
-                SDL_timers = SDL_timers->next;
-                SDL_free(freeme);
-            }
-            SDL_timer_running = 0;
-            list_changed = SDL_TRUE;
-        } else {
-            SDL_SYS_StopTimer();
-            SDL_timer_running = 0;
-        }
-    }
-    if (ms) {
-        if (SDL_timer_threaded) {
-            if (SDL_AddTimerInternal
-                (ms, callback_wrapper, (void *) callback) == NULL) {
-                retval = -1;
-            }
-        } else {
-            SDL_timer_running = 1;
-            SDL_alarm_interval = ms;
-            SDL_alarm_callback = callback;
-            retval = SDL_SYS_StartTimer();
-        }
-    }
-    if (SDL_timer_threaded) {
-        SDL_mutexV(SDL_timer_mutex);
-    }
-
-    return retval;
+    return canceled;
 }
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/SDL_timer_c.h	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/SDL_timer_c.h	Thu Jan 27 14:45:06 2011 -0800
@@ -27,21 +27,7 @@
 #define ROUND_RESOLUTION(X)	\
 	(((X+TIMER_RESOLUTION-1)/TIMER_RESOLUTION)*TIMER_RESOLUTION)
 
-extern int SDL_timer_started;
-extern int SDL_timer_running;
-
-/* Data to handle a single periodic alarm */
-extern Uint32 SDL_alarm_interval;
-extern SDL_TimerCallback SDL_alarm_callback;
-
-/* Set whether or not the timer should use a thread.
-   This should be called while the timer subsystem is running.
-*/
-extern int SDL_SetTimerThreaded(int value);
-
 extern int SDL_TimerInit(void);
 extern void SDL_TimerQuit(void);
 
-/* This function is called from the SDL event thread if it is available */
-extern void SDL_ThreadedTimerCheck(void);
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/beos/SDL_systimer.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/beos/SDL_systimer.c	Thu Jan 27 14:45:06 2011 -0800
@@ -25,9 +25,7 @@
 
 #include <be/kernel/OS.h>
 
-#include "SDL_thread.h"
 #include "SDL_timer.h"
-#include "../SDL_timer_c.h"
 
 static bigtime_t start;
 
@@ -50,55 +48,6 @@
     snooze(ms * 1000);
 }
 
-/* Data to handle a single periodic alarm */
-static int timer_alive = 0;
-static SDL_Thread *timer = NULL;
-
-static int
-RunTimer(void *unused)
-{
-    while (timer_alive) {
-        if (SDL_timer_running) {
-            SDL_ThreadedTimerCheck();
-        }
-        SDL_Delay(10);
-    }
-    return (0);
-}
+#endif /* SDL_TIMER_BEOS */
 
-/* This is only called if the event thread is not running */
-int
-SDL_SYS_TimerInit(void)
-{
-    timer_alive = 1;
-    timer = SDL_CreateThread(RunTimer, NULL);
-    if (timer == NULL)
-        return (-1);
-    return (SDL_SetTimerThreaded(1));
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    timer_alive = 0;
-    if (timer) {
-        SDL_WaitThread(timer, NULL);
-        timer = NULL;
-    }
-}
-
-int
-SDL_SYS_StartTimer(void)
-{
-    SDL_SetError("Internal logic error: BeOS uses threaded timer");
-    return (-1);
-}
-
-void
-SDL_SYS_StopTimer(void)
-{
-    return;
-}
-
-#endif /* SDL_TIMER_BEOS */
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/dummy/SDL_systimer.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/dummy/SDL_systimer.c	Thu Jan 27 14:45:06 2011 -0800
@@ -24,7 +24,6 @@
 #if defined(SDL_TIMER_DUMMY) || defined(SDL_TIMERS_DISABLED)
 
 #include "SDL_timer.h"
-#include "../SDL_timer_c.h"
 
 void
 SDL_StartTicks(void)
@@ -44,57 +43,6 @@
     SDL_Unsupported();
 }
 
-#include "SDL_thread.h"
-
-/* Data to handle a single periodic alarm */
-static int timer_alive = 0;
-static SDL_Thread *timer = NULL;
-
-static int
-RunTimer(void *unused)
-{
-    while (timer_alive) {
-        if (SDL_timer_running) {
-            SDL_ThreadedTimerCheck();
-        }
-        SDL_Delay(1);
-    }
-    return (0);
-}
+#endif /* SDL_TIMER_DUMMY || SDL_TIMERS_DISABLED */
 
-/* This is only called if the event thread is not running */
-int
-SDL_SYS_TimerInit(void)
-{
-    timer_alive = 1;
-    timer = SDL_CreateThread(RunTimer, NULL);
-    if (timer == NULL)
-        return (-1);
-    return (SDL_SetTimerThreaded(1));
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    timer_alive = 0;
-    if (timer) {
-        SDL_WaitThread(timer, NULL);
-        timer = NULL;
-    }
-}
-
-int
-SDL_SYS_StartTimer(void)
-{
-    SDL_SetError("Internal logic error: threaded timer in use");
-    return (-1);
-}
-
-void
-SDL_SYS_StopTimer(void)
-{
-    return;
-}
-
-#endif /* SDL_TIMER_DUMMY || SDL_TIMERS_DISABLED */
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/nds/SDL_systimer.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/nds/SDL_systimer.c	Thu Jan 27 14:45:06 2011 -0800
@@ -27,22 +27,25 @@
 #include <nds/timers.h>
 
 #include "SDL_timer.h"
-#include "../SDL_timer_c.h"
-#include "../SDL_systimer.h"
+
+
+static volatile Uint32 timer_ticks;
 
-/* Data to handle a single periodic alarm */
-static int timer_alive = 0;
-static Uint32 timer_ticks;
+static void
+NDS_TimerInterrupt(void)
+{
+    timer_ticks++;
+}
 
 void
 SDL_StartTicks(void)
 {
-    if (!timer_alive) {
-        SDL_SYS_TimerInit();
-        SDL_SYS_StartTimer();
-    }
+    timer_ticks = 0;
 
-    timer_ticks = 0;
+    TIMER_CR(3) = TIMER_DIV_1024 | TIMER_IRQ_REQ;
+    TIMER_DATA(3) = TIMER_FREQ_1024(1000);
+    irqSet(IRQ_TIMER3, NDS_TimerInterrupt);
+    irqEnable(IRQ_TIMER3);
 }
 
 Uint32
@@ -61,60 +64,6 @@
     }
 }
 
-static int
-RunTimer(void *unused)
-{
-    while (timer_alive) {
-        if (SDL_timer_running) {
-        }
-        SDL_Delay(1);
-    }
-    return (0);
-}
-
-void
-NDS_TimerInterrupt(void)
-{
-    timer_ticks++;
-}
+#endif /* SDL_TIMER_NDS */
 
-/* This is only called if the event thread is not running */
-int
-SDL_SYS_TimerInit(void)
-{
-    timer_alive = 1;
-    timer_ticks = 0;
-    TIMER_CR(3) = TIMER_DIV_1024 | TIMER_IRQ_REQ;
-    TIMER_DATA(3) = TIMER_FREQ_1024(1000);
-    irqSet(IRQ_TIMER3, NDS_TimerInterrupt);
-    irqEnable(IRQ_TIMER3);
-    return 0;
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    if (timer_alive) {
-        TIMER_CR(3) = 0;
-    }
-    timer_alive = 0;
-    irqDisable(IRQ_TIMER3);
-}
-
-int
-SDL_SYS_StartTimer(void)
-{
-    TIMER_CR(3) |= TIMER_ENABLE;
-    return 0;
-}
-
-void
-SDL_SYS_StopTimer(void)
-{
-    TIMER_CR(3) &= ~TIMER_ENABLE;
-    return;
-}
-
-
-#endif /* SDL_TIMER_NDS */
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/unix/SDL_systimer.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/unix/SDL_systimer.c	Thu Jan 27 14:45:06 2011 -0800
@@ -25,14 +25,10 @@
 
 #include <stdio.h>
 #include <sys/time.h>
-#include <signal.h>
 #include <unistd.h>
-#include <string.h>
 #include <errno.h>
 
 #include "SDL_timer.h"
-#include "../SDL_systimer.h"
-#include "../SDL_timer_c.h"
 
 /* The clock_gettime provides monotonous time, so we should use it if
    it's available. The clock_gettime function is behind ifdef
@@ -43,10 +39,6 @@
 #include <time.h>
 #endif
 
-#if SDL_THREADS_DISABLED
-#define USE_ITIMER
-#endif
-
 /* The first ticks value of the application */
 #ifdef HAVE_CLOCK_GETTIME
 static struct timespec start;
@@ -131,118 +123,6 @@
     } while (was_error && (errno == EINTR));
 }
 
-#ifdef USE_ITIMER
-
-static void
-HandleAlarm(int sig)
-{
-    Uint32 ms;
-
-    if (SDL_alarm_callback) {
-        ms = (*SDL_alarm_callback) (SDL_alarm_interval);
-        if (ms != SDL_alarm_interval) {
-            SDL_SetTimer(ms, SDL_alarm_callback);
-        }
-    }
-}
-
-int
-SDL_SYS_TimerInit(void)
-{
-    struct sigaction action;
-
-    /* Set the alarm handler (Linux specific) */
-    SDL_memset(&action, 0, sizeof(action));
-    action.sa_handler = HandleAlarm;
-    action.sa_flags = SA_RESTART;
-    sigemptyset(&action.sa_mask);
-    sigaction(SIGALRM, &action, NULL);
-    return (0);
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    SDL_SetTimer(0, NULL);
-}
-
-int
-SDL_SYS_StartTimer(void)
-{
-    struct itimerval timer;
-
-    timer.it_value.tv_sec = (SDL_alarm_interval / 1000);
-    timer.it_value.tv_usec = (SDL_alarm_interval % 1000) * 1000;
-    timer.it_interval.tv_sec = (SDL_alarm_interval / 1000);
-    timer.it_interval.tv_usec = (SDL_alarm_interval % 1000) * 1000;
-    setitimer(ITIMER_REAL, &timer, NULL);
-    return (0);
-}
-
-void
-SDL_SYS_StopTimer(void)
-{
-    struct itimerval timer;
-
-    SDL_memset(&timer, 0, (sizeof timer));
-    setitimer(ITIMER_REAL, &timer, NULL);
-}
+#endif /* SDL_TIMER_UNIX */
 
-#else /* USE_ITIMER */
-
-#include "SDL_thread.h"
-
-/* Data to handle a single periodic alarm */
-static int timer_alive = 0;
-static SDL_Thread *timer = NULL;
-
-static int
-RunTimer(void *unused)
-{
-    while (timer_alive) {
-        if (SDL_timer_running) {
-            SDL_ThreadedTimerCheck();
-        }
-        SDL_Delay(1);
-    }
-    return (0);
-}
-
-/* This is only called if the event thread is not running */
-int
-SDL_SYS_TimerInit(void)
-{
-    timer_alive = 1;
-    timer = SDL_CreateThread(RunTimer, NULL);
-    if (timer == NULL)
-        return (-1);
-    return (SDL_SetTimerThreaded(1));
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    timer_alive = 0;
-    if (timer) {
-        SDL_WaitThread(timer, NULL);
-        timer = NULL;
-    }
-}
-
-int
-SDL_SYS_StartTimer(void)
-{
-    SDL_SetError("Internal logic error: Linux uses threaded timer");
-    return (-1);
-}
-
-void
-SDL_SYS_StopTimer(void)
-{
-    return;
-}
-
-#endif /* USE_ITIMER */
-
-#endif /* SDL_TIMER_UNIX */
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/wince/SDL_systimer.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/wince/SDL_systimer.c	Thu Jan 27 14:45:06 2011 -0800
@@ -24,11 +24,8 @@
 #ifdef SDL_TIMER_WINCE
 
 #include "../../core/windows/SDL_windows.h"
-#include <mmsystem.h>
 
-#include "SDL_thread.h"
 #include "SDL_timer.h"
-#include "../SDL_timer_c.h"
 
 static Uint64 start_date;
 static Uint64 start_ticks;
@@ -69,6 +66,14 @@
     return ((Sint32) (wce_date() - start_date));
 }
 
+/* Recard start-time of application for reference */
+void
+SDL_StartTicks(void)
+{
+    start_date = wce_date();
+    start_ticks = wce_ticks();
+}
+
 /* Return time in ms relative to when SDL was started */
 Uint32
 SDL_GetTicks()
@@ -89,122 +94,6 @@
     Sleep(ms);
 }
 
-/* Recard start-time of application for reference */
-void
-SDL_StartTicks(void)
-{
-    start_date = wce_date();
-    start_ticks = wce_ticks();
-}
-
-static UINT WIN_timer;
-
-#if ( _WIN32_WCE <= 420 )
-
-static HANDLE timersThread = 0;
-static HANDLE timersQuitEvent = 0;
-
-DWORD
-TimersThreadProc(void *data)
-{
-    while (WaitForSingleObject(timersQuitEvent, 10) == WAIT_TIMEOUT) {
-        SDL_ThreadedTimerCheck();
-    }
-    return 0;
-}
-
-int
-SDL_SYS_TimerInit(void)
-{
-    // create a thread to process a threaded timers
-    // SetTimer does not suit the needs because 
-    // TimerCallbackProc will be called only when WM_TIMER occured
-
-    timersQuitEvent = CreateEvent(0, TRUE, FALSE, 0);
-    if (!timersQuitEvent) {
-        SDL_SetError("Cannot create event for timers thread");
-        return -1;
-    }
-    timersThread = CreateThread(NULL, 0, TimersThreadProc, 0, 0, 0);
-    if (!timersThread) {
-        SDL_SetError
-            ("Cannot create timers thread, check amount of RAM available");
-        return -1;
-    }
-    SetThreadPriority(timersThread, THREAD_PRIORITY_HIGHEST);
-
-    return (SDL_SetTimerThreaded(1));
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    SetEvent(timersQuitEvent);
-    if (WaitForSingleObject(timersThread, 2000) == WAIT_TIMEOUT)
-        TerminateThread(timersThread, 0);
-    CloseHandle(timersThread);
-    CloseHandle(timersQuitEvent);
-    return;
-}
-
-#else
+#endif /* SDL_TIMER_WINCE */
 
-#pragma comment(lib, "mmtimer.lib")
-
-/* Data to handle a single periodic alarm */
-static UINT timerID = 0;
-
-static void CALLBACK
-HandleAlarm(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
-{
-    SDL_ThreadedTimerCheck();
-}
-
-
-int
-SDL_SYS_TimerInit(void)
-{
-    MMRESULT result;
-
-    /* Set timer resolution */
-    result = timeBeginPeriod(TIMER_RESOLUTION);
-    if (result != TIMERR_NOERROR) {
-        SDL_SetError("Warning: Can't set %d ms timer resolution",
-                     TIMER_RESOLUTION);
-    }
-    /* Allow 10 ms of drift so we don't chew on CPU */
-    timerID =
-        timeSetEvent(TIMER_RESOLUTION, 1, HandleAlarm, 0, TIME_PERIODIC);
-    if (!timerID) {
-        SDL_SetError("timeSetEvent() failed");
-        return (-1);
-    }
-    return (SDL_SetTimerThreaded(1));
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    if (timerID) {
-        timeKillEvent(timerID);
-    }
-    timeEndPeriod(TIMER_RESOLUTION);
-}
-
-#endif
-
-int
-SDL_SYS_StartTimer(void)
-{
-    SDL_SetError("Internal logic error: WinCE uses threaded timer");
-    return (-1);
-}
-
-void
-SDL_SYS_StopTimer(void)
-{
-    return;
-}
-
-#endif /* SDL_TIMER_WINCE */
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/timer/windows/SDL_systimer.c	Thu Jan 27 10:40:17 2011 -0800
+++ b/src/timer/windows/SDL_systimer.c	Thu Jan 27 14:45:06 2011 -0800
@@ -24,10 +24,8 @@
 #ifdef SDL_TIMER_WINDOWS
 
 #include "../../core/windows/SDL_windows.h"
-#include <mmsystem.h>
 
 #include "SDL_timer.h"
-#include "../SDL_timer_c.h"
 
 #ifdef _WIN32_WCE
 #error This is WinCE. Please use src/timer/wince/SDL_systimer.c instead.
@@ -106,60 +104,6 @@
     Sleep(ms);
 }
 
-/* Data to handle a single periodic alarm */
-static UINT timerID = 0;
-
-static void CALLBACK
-HandleAlarm(UINT uID, UINT uMsg, DWORD_PTR dwUser,
-            DWORD_PTR dw1, DWORD_PTR dw2)
-{
-    SDL_ThreadedTimerCheck();
-}
-
-
-int
-SDL_SYS_TimerInit(void)
-{
-    MMRESULT result;
-
-    /* Set timer resolution */
-    result = timeBeginPeriod(TIMER_RESOLUTION);
-    if (result != TIMERR_NOERROR) {
-        SDL_SetError("Warning: Can't set %d ms timer resolution",
-                     TIMER_RESOLUTION);
-    }
-    /* Allow 10 ms of drift so we don't chew on CPU */
-    timerID =
-        timeSetEvent(TIMER_RESOLUTION, 1, HandleAlarm, 0, TIME_PERIODIC);
-    if (!timerID) {
-        SDL_SetError("timeSetEvent() failed");
-        return (-1);
-    }
-    return (SDL_SetTimerThreaded(1));
-}
-
-void
-SDL_SYS_TimerQuit(void)
-{
-    if (timerID) {
-        timeKillEvent(timerID);
-    }
-    timeEndPeriod(TIMER_RESOLUTION);
-}
-
-int
-SDL_SYS_StartTimer(void)
-{
-    SDL_SetError("Internal logic error: Win32 uses threaded timer");
-    return (-1);
-}
-
-void
-SDL_SYS_StopTimer(void)
-{
-    return;
-}
-
 #endif /* SDL_TIMER_WINDOWS */
 
 /* vi: set ts=4 sw=4 expandtab: */