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 (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; }
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback 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"); } return NULL; } if ( ! SDL_timer_threaded ) { SDL_SetError("Multiple timers require threaded events!"); return NULL; } SDL_mutexP(SDL_timer_mutex); t = SDL_AddTimerInternal(interval, callback, param); SDL_mutexV(SDL_timer_mutex); return t; }
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; /* 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 0; }