SDL  2.0
SDL_timer.c File Reference
#include "../SDL_internal.h"
#include "SDL_timer.h"
#include "SDL_timer_c.h"
#include "SDL_atomic.h"
#include "SDL_cpuinfo.h"
#include "../thread/SDL_systhread.h"
+ Include dependency graph for SDL_timer.c:

Go to the source code of this file.

Data Structures

struct  SDL_Timer
struct  SDL_TimerMap
struct  SDL_TimerData

Functions

static void SDL_AddTimerInternal (SDL_TimerData *data, SDL_Timer *timer)
static int SDL_TimerThread (void *_data)
int SDL_TimerInit (void)
void SDL_TimerQuit (void)
SDL_TimerID SDL_AddTimer (Uint32 interval, SDL_TimerCallback callback, void *param)
 Add a new timer to the pool of timers already running.
SDL_bool SDL_RemoveTimer (SDL_TimerID id)
 Remove a timer knowing its ID.

Variables

static SDL_TimerData SDL_timer_data

Function Documentation

SDL_TimerID SDL_AddTimer ( Uint32  interval,
SDL_TimerCallback  callback,
void param 
)

Add a new timer to the pool of timers already running.

Returns
A timer ID, or 0 when an error occurs.

Definition at line 279 of file SDL_timer.c.

References SDL_TimerData::active, SDL_Timer::callback, callback(), SDL_Timer::canceled, SDL_TimerData::freelist, SDL_Timer::interval, SDL_TimerData::lock, SDL_Timer::next, SDL_TimerMap::next, SDL_TimerData::nextID, SDL_Timer::param, SDL_TimerData::pending, SDL_Timer::scheduled, SDL_AtomicGet, SDL_AtomicIncRef, SDL_AtomicLock, SDL_AtomicSet, SDL_AtomicUnlock, SDL_free, SDL_GetTicks(), SDL_LockMutex, SDL_malloc, SDL_OutOfMemory, SDL_RemoveTimer, SDL_SemPost, SDL_timer_data, SDL_TimerInit(), SDL_UnlockMutex, SDL_TimerData::sem, SDL_TimerMap::timer, SDL_Timer::timerID, SDL_TimerMap::timerID, SDL_TimerData::timermap, and SDL_TimerData::timermap_lock.

{
SDL_Timer *timer;
SDL_TimerMap *entry;
if (!SDL_AtomicGet(&data->active)) {
if (SDL_TimerInit() < 0) {
return 0;
}
}
timer = data->freelist;
if (timer) {
data->freelist = timer->next;
}
if (timer) {
} else {
timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
if (!timer) {
return 0;
}
}
timer->timerID = SDL_AtomicIncRef(&data->nextID);
timer->callback = callback;
timer->param = param;
timer->interval = interval;
timer->scheduled = SDL_GetTicks() + interval;
SDL_AtomicSet(&timer->canceled, 0);
entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
if (!entry) {
SDL_free(timer);
return 0;
}
entry->timer = timer;
entry->timerID = timer->timerID;
entry->next = data->timermap;
data->timermap = entry;
/* Add the timer to the pending list for the timer thread */
timer->next = data->pending;
data->pending = timer;
/* Wake up the timer thread if necessary */
SDL_SemPost(data->sem);
return entry->timerID;
}
static void SDL_AddTimerInternal ( SDL_TimerData data,
SDL_Timer timer 
)
static

Definition at line 80 of file SDL_timer.c.

References SDL_Timer::next, NULL, SDL_Timer::scheduled, and SDL_TimerData::timers.

Referenced by SDL_TimerThread().

{
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;
}
SDL_bool SDL_RemoveTimer ( SDL_TimerID  id)

Remove a timer knowing its ID.

Returns
A boolean value indicating success or failure.
Warning
It is not safe to remove a timer multiple times.

Definition at line 342 of file SDL_timer.c.

References SDL_Timer::canceled, SDL_TimerMap::next, NULL, SDL_AtomicGet, SDL_AtomicSet, SDL_FALSE, SDL_free, SDL_LockMutex, SDL_timer_data, SDL_TRUE, SDL_UnlockMutex, SDL_TimerMap::timer, SDL_TimerMap::timerID, SDL_TimerData::timermap, and SDL_TimerData::timermap_lock.

{
SDL_TimerMap *prev, *entry;
SDL_bool canceled = SDL_FALSE;
/* Find the timer */
prev = NULL;
for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
if (entry->timerID == id) {
if (prev) {
prev->next = entry->next;
} else {
data->timermap = entry->next;
}
break;
}
}
if (entry) {
if (!SDL_AtomicGet(&entry->timer->canceled)) {
canceled = SDL_TRUE;
}
SDL_free(entry);
}
return canceled;
}
int SDL_TimerInit ( void  )

Definition at line 207 of file SDL_timer.c.

References SDL_TimerData::active, SDL_TimerData::nextID, SDL_AtomicGet, SDL_AtomicSet, SDL_CreateMutex, SDL_CreateSemaphore, SDL_CreateThreadInternal(), SDL_DestroyMutex, SDL_timer_data, SDL_TimerQuit(), SDL_TimerThread(), SDL_TimerData::sem, SDL_TimerData::thread, and SDL_TimerData::timermap_lock.

Referenced by SDL_AddTimer(), and SDL_InitSubSystem().

{
if (!SDL_AtomicGet(&data->active)) {
const char *name = "SDLTimer";
if (!data->timermap_lock) {
return -1;
}
if (!data->sem) {
return -1;
}
SDL_AtomicSet(&data->active, 1);
/* Timer threads use a callback into the app, so we can't set a limited stack size here. */
if (!data->thread) {
return -1;
}
SDL_AtomicSet(&data->nextID, 1);
}
return 0;
}
void SDL_TimerQuit ( void  )

Definition at line 239 of file SDL_timer.c.

References SDL_TimerData::active, SDL_TimerData::freelist, SDL_Timer::next, SDL_TimerMap::next, NULL, SDL_AtomicCAS, SDL_DestroyMutex, SDL_DestroySemaphore, SDL_free, SDL_SemPost, SDL_timer_data, SDL_WaitThread, SDL_TimerData::sem, SDL_TimerData::thread, SDL_TimerData::timermap, SDL_TimerData::timermap_lock, and SDL_TimerData::timers.

Referenced by SDL_QuitSubSystem(), and SDL_TimerInit().

{
SDL_Timer *timer;
SDL_TimerMap *entry;
if (SDL_AtomicCAS(&data->active, 1, 0)) { /* active? Move to inactive. */
/* Shutdown the timer thread */
if (data->thread) {
SDL_SemPost(data->sem);
data->thread = NULL;
}
data->sem = NULL;
/* 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);
}
}
}
static int SDL_TimerThread ( void _data)
static

Definition at line 101 of file SDL_timer.c.

References SDL_TimerData::active, SDL_Timer::callback, SDL_Timer::canceled, SDL_TimerData::freelist, SDL_Timer::interval, SDL_TimerData::lock, SDL_Timer::next, NULL, SDL_Timer::param, SDL_TimerData::pending, SDL_Timer::scheduled, SDL_AddTimerInternal(), SDL_AtomicGet, SDL_AtomicLock, SDL_AtomicSet, SDL_AtomicUnlock, SDL_GetTicks(), SDL_MUTEX_MAXWAIT, SDL_SemWaitTimeout, SDL_TimerData::sem, and SDL_TimerData::timers.

Referenced by SDL_TimerInit().

{
SDL_Timer *pending;
SDL_Timer *current;
SDL_Timer *freelist_head = NULL;
SDL_Timer *freelist_tail = NULL;
Uint32 tick, now, interval, delay;
/* 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 */
{
/* 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;
}
}
/* 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 (!SDL_AtomicGet(&data->active)) {
break;
}
/* Initial delay if there are no timers */
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 (SDL_AtomicGet(&current->canceled)) {
interval = 0;
} else {
interval = current->callback(current->interval, current->param);
}
if (interval > 0) {
/* Reschedule this timer */
current->interval = interval;
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;
SDL_AtomicSet(&current->canceled, 1);
}
}
/* 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 0;
}

Variable Documentation

SDL_TimerData SDL_timer_data
static

Definition at line 71 of file SDL_timer.c.

Referenced by SDL_AddTimer(), SDL_RemoveTimer(), SDL_TimerInit(), and SDL_TimerQuit().