From dc76b82f7aeb2b371733840b7f2abcc1128728c1 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 27 Jan 2011 14:45:06 -0800 Subject: [PATCH] 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 --- include/SDL_compat.h | 7 + include/SDL_timer.h | 86 +---- src/SDL_compat.c | 25 ++ src/events/SDL_events.c | 6 - src/timer/SDL_systimer.h | 42 --- src/timer/SDL_timer.c | 537 +++++++++++++++++-------------- src/timer/SDL_timer_c.h | 14 - src/timer/beos/SDL_systimer.c | 53 +-- src/timer/dummy/SDL_systimer.c | 54 +--- src/timer/nds/SDL_systimer.c | 79 +---- src/timer/unix/SDL_systimer.c | 122 +------ src/timer/wince/SDL_systimer.c | 129 +------- src/timer/windows/SDL_systimer.c | 56 ---- 13 files changed, 378 insertions(+), 832 deletions(-) delete mode 100644 src/timer/SDL_systimer.h diff --git a/include/SDL_compat.h b/include/SDL_compat.h index 92f622be8..1e2a53917 100644 --- a/include/SDL_compat.h +++ b/include/SDL_compat.h @@ -344,6 +344,13 @@ typedef SDL_Window* SDL_WindowID; #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*/ diff --git a/include/SDL_timer.h b/include/SDL_timer.h index 91160f6f9..2d38a0689 100644 --- a/include/SDL_timer.h +++ b/include/SDL_timer.h @@ -41,104 +41,50 @@ extern "C" { #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 - */ -/*@{*/ - -/** - * 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 diff --git a/src/SDL_compat.c b/src/SDL_compat.c index c35dfbb66..fedd84520 100644 --- a/src/SDL_compat.c +++ b/src/SDL_compat.c @@ -1760,6 +1760,31 @@ SDL_EnableUNICODE(int enable) 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) { diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 06f533cd2..b3c2ad818 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -123,9 +123,6 @@ SDL_GobbleEvents(void *unused) /* 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_GobbleEvents(void *unused) SDL_EventLock.safe = 0; SDL_mutexV(SDL_EventLock.lock); } - SDL_SetTimerThreaded(0); event_thread = 0; return (0); } @@ -168,8 +164,6 @@ SDL_StartEventThread(Uint32 flags) } 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 = diff --git a/src/timer/SDL_systimer.h b/src/timer/SDL_systimer.h deleted file mode 100644 index d0cfd25db..000000000 --- a/src/timer/SDL_systimer.h +++ /dev/null @@ -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: */ diff --git a/src/timer/SDL_timer.c b/src/timer/SDL_timer.c index 684a20ff7..593add64d 100644 --- a/src/timer/SDL_timer.c +++ b/src/timer/SDL_timer.c @@ -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; -/* Data to handle a single periodic alarm */ -Uint32 SDL_alarm_interval = 0; -SDL_TimerCallback SDL_alarm_callback; +typedef struct _SDL_TimerMap +{ + int timerID; + SDL_Timer *timer; + struct _SDL_TimerMap *next; +} SDL_TimerMap; -/* Data used for a thread-based timer */ -static int SDL_timer_threaded = 0; +/* A reasonable guess */ +#define CACHELINE_SIZE 128 -struct _SDL_TimerID -{ - Uint32 interval; - SDL_NewTimerCallback cb; - void *param; - Uint32 last_alarm; - struct _SDL_TimerID *next; -}; +/* 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; -static SDL_TimerID SDL_timers = NULL; -static SDL_mutex *SDL_timer_mutex; -static volatile SDL_bool list_changed = SDL_FALSE; + /* Padding to separate cache lines between threads */ + char pad[CACHELINE_SIZE]; -/* 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) + /* 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; + + /* List of timers - this is only touched by the timer thread */ + SDL_Timer *timers; +} SDL_TimerData; + +static SDL_TimerData SDL_timer_data; + + +/* 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) { - int retval; + SDL_Timer *prev, *curr; - if (SDL_timer_started) { - SDL_SetError("Timer already initialized"); - retval = -1; + 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 { - retval = 0; - SDL_timer_threaded = value; + data->timers = timer; } - return retval; + timer->next = curr; } -int -SDL_TimerInit(void) +static int +SDL_TimerThread(void *_data) { - int retval; + 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; - retval = 0; - if (SDL_timer_started) { - SDL_TimerQuit(); - } - if (!SDL_timer_threaded) { - retval = SDL_SYS_TimerInit(); - } - if (SDL_timer_threaded) { - SDL_timer_mutex = SDL_CreateMutex(); - } - if (retval == 0) { - SDL_timer_started = 1; + /* 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; + + 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 (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; } -void -SDL_TimerQuit(void) +int +SDL_TimerInit(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_TimerData *data = &SDL_timer_data; + + if (!data->active) { + data->timermap_lock = SDL_CreateMutex(); + if (!data->timermap_lock) { + return -1; + } + + 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); } - SDL_timer_started = 0; - SDL_timer_threaded = 0; + return 0; } void -SDL_ThreadedTimerCheck(void) +SDL_TimerQuit(void) { - Uint32 now, ms; - SDL_TimerID t, prev, next; - SDL_bool removed; - - SDL_mutexP(SDL_timer_mutex); - - 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; - - 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; - } + SDL_TimerData *data = &SDL_timer_data; + SDL_Timer *timer; + SDL_TimerMap *entry; + + if (data->active) { + data->active = SDL_FALSE; + + /* Shutdown the timer thread */ + if (data->thread) { + SDL_SemPost(data->sem); + SDL_WaitThread(data->thread, NULL); + data->thread = NULL; } - } while (list_changed); - SDL_mutexV(SDL_timer_mutex); -} + SDL_DestroySemaphore(data->sem); + data->sem = NULL; -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; + /* Clean up the timer entries */ + while (data->timers) { + timer = data->timers; + data->timers = timer->next; + SDL_free(timer); + } + 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); + } + + 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; } - if (!SDL_timer_threaded) { - SDL_SetError("Multiple timers require threaded events!"); - return NULL; + + SDL_AtomicLock(&data->lock); + timer = data->freelist; + if (timer) { + data->freelist = timer->next; } - SDL_mutexP(SDL_timer_mutex); - t = SDL_AddTimerInternal(interval, callback, param); - SDL_mutexV(SDL_timer_mutex); - return t; + SDL_AtomicUnlock(&data->lock); + + if (timer) { + SDL_RemoveTimer(timer->timerID); + } else { + timer = (SDL_Timer *)SDL_malloc(sizeof(*timer)); + if (!timer) { + SDL_OutOfMemory(); + return 0; + } + } + 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; + } + 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; - - 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) { + SDL_TimerData *data = &SDL_timer_data; + SDL_TimerMap *prev, *entry; + SDL_bool canceled = SDL_FALSE; + + /* 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; -} - -/* 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); -} + SDL_mutexV(data->timermap_lock); -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 (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 (entry) { + if (!entry->timer->canceled) { + entry->timer->canceled = SDL_TRUE; + canceled = SDL_TRUE; } + SDL_free(entry); } - 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: */ diff --git a/src/timer/SDL_timer_c.h b/src/timer/SDL_timer_c.h index 3e7d3ebd7..c99ccef72 100644 --- a/src/timer/SDL_timer_c.h +++ b/src/timer/SDL_timer_c.h @@ -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: */ diff --git a/src/timer/beos/SDL_systimer.c b/src/timer/beos/SDL_systimer.c index e31bdebea..cec78deb0 100644 --- a/src/timer/beos/SDL_systimer.c +++ b/src/timer/beos/SDL_systimer.c @@ -25,9 +25,7 @@ #include -#include "SDL_thread.h" #include "SDL_timer.h" -#include "../SDL_timer_c.h" static bigtime_t start; @@ -50,55 +48,6 @@ SDL_Delay(Uint32 ms) 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); -} - -/* 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: */ diff --git a/src/timer/dummy/SDL_systimer.c b/src/timer/dummy/SDL_systimer.c index f13f0d566..77f69c395 100644 --- a/src/timer/dummy/SDL_systimer.c +++ b/src/timer/dummy/SDL_systimer.c @@ -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_Delay(Uint32 ms) 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); -} - -/* 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: */ diff --git a/src/timer/nds/SDL_systimer.c b/src/timer/nds/SDL_systimer.c index 50791f887..75abf2d65 100644 --- a/src/timer/nds/SDL_systimer.c +++ b/src/timer/nds/SDL_systimer.c @@ -27,22 +27,25 @@ #include #include "SDL_timer.h" -#include "../SDL_timer_c.h" -#include "../SDL_systimer.h" -/* Data to handle a single periodic alarm */ -static int timer_alive = 0; -static Uint32 timer_ticks; + +static volatile 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_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 @@ SDL_Delay(Uint32 ms) } } -static int -RunTimer(void *unused) -{ - while (timer_alive) { - if (SDL_timer_running) { - } - SDL_Delay(1); - } - return (0); -} - -void -NDS_TimerInterrupt(void) -{ - timer_ticks++; -} - -/* 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: */ diff --git a/src/timer/unix/SDL_systimer.c b/src/timer/unix/SDL_systimer.c index 2a933eea2..ca7f45a8d 100644 --- a/src/timer/unix/SDL_systimer.c +++ b/src/timer/unix/SDL_systimer.c @@ -25,14 +25,10 @@ #include #include -#include #include -#include #include #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 #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 @@ SDL_Delay(Uint32 ms) } 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); -} - -#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: */ diff --git a/src/timer/wince/SDL_systimer.c b/src/timer/wince/SDL_systimer.c index 5abbaa587..8a2ced4e3 100644 --- a/src/timer/wince/SDL_systimer.c +++ b/src/timer/wince/SDL_systimer.c @@ -24,11 +24,8 @@ #ifdef SDL_TIMER_WINCE #include "../../core/windows/SDL_windows.h" -#include -#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 @@ wce_rel_date(void) 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 @@ SDL_Delay(Uint32 ms) 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 - -#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: */ diff --git a/src/timer/windows/SDL_systimer.c b/src/timer/windows/SDL_systimer.c index 0ae01da10..2674e2200 100644 --- a/src/timer/windows/SDL_systimer.c +++ b/src/timer/windows/SDL_systimer.c @@ -24,10 +24,8 @@ #ifdef SDL_TIMER_WINDOWS #include "../../core/windows/SDL_windows.h" -#include #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 @@ SDL_Delay(Uint32 ms) 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: */