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. More...
 
SDL_bool SDL_RemoveTimer (SDL_TimerID id)
 Remove a timer knowing its ID. More...
 

Variables

static SDL_TimerData SDL_timer_data
 

Function Documentation

◆ SDL_AddTimer()

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.

280{
282 SDL_Timer *timer;
283 SDL_TimerMap *entry;
284
285 SDL_AtomicLock(&data->lock);
286 if (!SDL_AtomicGet(&data->active)) {
287 if (SDL_TimerInit() < 0) {
288 SDL_AtomicUnlock(&data->lock);
289 return 0;
290 }
291 }
292
293 timer = data->freelist;
294 if (timer) {
295 data->freelist = timer->next;
296 }
297 SDL_AtomicUnlock(&data->lock);
298
299 if (timer) {
300 SDL_RemoveTimer(timer->timerID);
301 } else {
302 timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
303 if (!timer) {
305 return 0;
306 }
307 }
308 timer->timerID = SDL_AtomicIncRef(&data->nextID);
309 timer->callback = callback;
310 timer->param = param;
311 timer->interval = interval;
312 timer->scheduled = SDL_GetTicks() + interval;
313 SDL_AtomicSet(&timer->canceled, 0);
314
315 entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
316 if (!entry) {
317 SDL_free(timer);
319 return 0;
320 }
321 entry->timer = timer;
322 entry->timerID = timer->timerID;
323
324 SDL_LockMutex(data->timermap_lock);
325 entry->next = data->timermap;
326 data->timermap = entry;
327 SDL_UnlockMutex(data->timermap_lock);
328
329 /* Add the timer to the pending list for the timer thread */
330 SDL_AtomicLock(&data->lock);
331 timer->next = data->pending;
332 data->pending = timer;
333 SDL_AtomicUnlock(&data->lock);
334
335 /* Wake up the timer thread if necessary */
336 SDL_SemPost(data->sem);
337
338 return entry->timerID;
339}
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
#define SDL_AtomicSet
#define SDL_AtomicLock
#define SDL_SemPost
#define SDL_LockMutex
#define SDL_malloc
#define SDL_AtomicUnlock
#define SDL_free
#define SDL_AtomicGet
#define SDL_UnlockMutex
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLfloat param
static SDL_TimerData SDL_timer_data
Definition: SDL_timer.c:71
SDL_bool SDL_RemoveTimer(SDL_TimerID id)
Remove a timer knowing its ID.
Definition: SDL_timer.c:342
int SDL_TimerInit(void)
Definition: SDL_timer.c:207
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Uint32 scheduled
Definition: SDL_timer.c:37
SDL_atomic_t canceled
Definition: SDL_timer.c:38
struct _SDL_Timer * next
Definition: SDL_timer.c:39
void * param
Definition: SDL_timer.c:35
SDL_TimerCallback callback
Definition: SDL_timer.c:34
Uint32 interval
Definition: SDL_timer.c:36
int timerID
Definition: SDL_timer.c:33
struct _SDL_TimerMap * next
Definition: SDL_timer.c:46
SDL_Timer * timer
Definition: SDL_timer.c:45
static Uint32 callback(Uint32 interval, void *param)
Definition: testtimer.c:34

References SDL_Timer::callback, callback(), SDL_Timer::canceled, SDL_Timer::interval, SDL_Timer::next, SDL_TimerMap::next, SDL_Timer::param, 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_TimerMap::timer, SDL_Timer::timerID, and SDL_TimerMap::timerID.

◆ SDL_AddTimerInternal()

static void SDL_AddTimerInternal ( SDL_TimerData data,
SDL_Timer timer 
)
static

Definition at line 80 of file SDL_timer.c.

81{
82 SDL_Timer *prev, *curr;
83
84 prev = NULL;
85 for (curr = data->timers; curr; prev = curr, curr = curr->next) {
86 if ((Sint32)(timer->scheduled-curr->scheduled) < 0) {
87 break;
88 }
89 }
90
91 /* Insert the timer here! */
92 if (prev) {
93 prev->next = timer;
94 } else {
95 data->timers = timer;
96 }
97 timer->next = curr;
98}
int32_t Sint32
Definition: SDL_stdinc.h:197
#define NULL
Definition: begin_code.h:167

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

Referenced by SDL_TimerThread().

◆ SDL_RemoveTimer()

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.

343{
345 SDL_TimerMap *prev, *entry;
346 SDL_bool canceled = SDL_FALSE;
347
348 /* Find the timer */
349 SDL_LockMutex(data->timermap_lock);
350 prev = NULL;
351 for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
352 if (entry->timerID == id) {
353 if (prev) {
354 prev->next = entry->next;
355 } else {
356 data->timermap = entry->next;
357 }
358 break;
359 }
360 }
361 SDL_UnlockMutex(data->timermap_lock);
362
363 if (entry) {
364 if (!SDL_AtomicGet(&entry->timer->canceled)) {
365 SDL_AtomicSet(&entry->timer->canceled, 1);
366 canceled = SDL_TRUE;
367 }
368 SDL_free(entry);
369 }
370 return canceled;
371}
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163

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, and SDL_TimerMap::timerID.

Referenced by SDL_AddTimer().

◆ SDL_TimerInit()

int SDL_TimerInit ( void  )

Definition at line 207 of file SDL_timer.c.

208{
210
211 if (!SDL_AtomicGet(&data->active)) {
212 const char *name = "SDLTimer";
214 if (!data->timermap_lock) {
215 return -1;
216 }
217
218 data->sem = SDL_CreateSemaphore(0);
219 if (!data->sem) {
220 SDL_DestroyMutex(data->timermap_lock);
221 return -1;
222 }
223
224 SDL_AtomicSet(&data->active, 1);
225
226 /* Timer threads use a callback into the app, so we can't set a limited stack size here. */
228 if (!data->thread) {
230 return -1;
231 }
232
233 SDL_AtomicSet(&data->nextID, 1);
234 }
235 return 0;
236}
#define SDL_CreateMutex
#define SDL_CreateSemaphore
#define SDL_DestroyMutex
GLuint const GLchar * name
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
void SDL_TimerQuit(void)
Definition: SDL_timer.c:239
static int SDL_TimerThread(void *_data)
Definition: SDL_timer.c:101
SDL_mutex * timermap_lock
Definition: SDL_timer.c:55

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

Referenced by SDL_AddTimer(), and SDL_InitSubSystem().

◆ SDL_TimerQuit()

void SDL_TimerQuit ( void  )

Definition at line 239 of file SDL_timer.c.

240{
242 SDL_Timer *timer;
243 SDL_TimerMap *entry;
244
245 if (SDL_AtomicCAS(&data->active, 1, 0)) { /* active? Move to inactive. */
246 /* Shutdown the timer thread */
247 if (data->thread) {
248 SDL_SemPost(data->sem);
249 SDL_WaitThread(data->thread, NULL);
250 data->thread = NULL;
251 }
252
254 data->sem = NULL;
255
256 /* Clean up the timer entries */
257 while (data->timers) {
258 timer = data->timers;
259 data->timers = timer->next;
260 SDL_free(timer);
261 }
262 while (data->freelist) {
263 timer = data->freelist;
264 data->freelist = timer->next;
265 SDL_free(timer);
266 }
267 while (data->timermap) {
268 entry = data->timermap;
269 data->timermap = entry->next;
270 SDL_free(entry);
271 }
272
273 SDL_DestroyMutex(data->timermap_lock);
274 data->timermap_lock = NULL;
275 }
276}
#define SDL_AtomicCAS
#define SDL_DestroySemaphore
#define SDL_WaitThread

References SDL_Timer::next, SDL_TimerMap::next, NULL, SDL_AtomicCAS, SDL_DestroyMutex, SDL_DestroySemaphore, SDL_free, SDL_SemPost, SDL_timer_data, and SDL_WaitThread.

Referenced by SDL_QuitSubSystem(), and SDL_TimerInit().

◆ SDL_TimerThread()

static int SDL_TimerThread ( void _data)
static

Definition at line 101 of file SDL_timer.c.

102{
103 SDL_TimerData *data = (SDL_TimerData *)_data;
104 SDL_Timer *pending;
105 SDL_Timer *current;
106 SDL_Timer *freelist_head = NULL;
107 SDL_Timer *freelist_tail = NULL;
108 Uint32 tick, now, interval, delay;
109
110 /* Threaded timer loop:
111 * 1. Queue timers added by other threads
112 * 2. Handle any timers that should dispatch this cycle
113 * 3. Wait until next dispatch time or new timer arrives
114 */
115 for ( ; ; ) {
116 /* Pending and freelist maintenance */
117 SDL_AtomicLock(&data->lock);
118 {
119 /* Get any timers ready to be queued */
120 pending = data->pending;
121 data->pending = NULL;
122
123 /* Make any unused timer structures available */
124 if (freelist_head) {
125 freelist_tail->next = data->freelist;
126 data->freelist = freelist_head;
127 }
128 }
129 SDL_AtomicUnlock(&data->lock);
130
131 /* Sort the pending timers into our list */
132 while (pending) {
133 current = pending;
134 pending = pending->next;
135 SDL_AddTimerInternal(data, current);
136 }
137 freelist_head = NULL;
138 freelist_tail = NULL;
139
140 /* Check to see if we're still running, after maintenance */
141 if (!SDL_AtomicGet(&data->active)) {
142 break;
143 }
144
145 /* Initial delay if there are no timers */
146 delay = SDL_MUTEX_MAXWAIT;
147
148 tick = SDL_GetTicks();
149
150 /* Process all the pending timers for this tick */
151 while (data->timers) {
152 current = data->timers;
153
154 if ((Sint32)(tick-current->scheduled) < 0) {
155 /* Scheduled for the future, wait a bit */
156 delay = (current->scheduled - tick);
157 break;
158 }
159
160 /* We're going to do something with this timer */
161 data->timers = current->next;
162
163 if (SDL_AtomicGet(&current->canceled)) {
164 interval = 0;
165 } else {
166 interval = current->callback(current->interval, current->param);
167 }
168
169 if (interval > 0) {
170 /* Reschedule this timer */
171 current->interval = interval;
172 current->scheduled = tick + interval;
173 SDL_AddTimerInternal(data, current);
174 } else {
175 if (!freelist_head) {
176 freelist_head = current;
177 }
178 if (freelist_tail) {
179 freelist_tail->next = current;
180 }
181 freelist_tail = current;
182
183 SDL_AtomicSet(&current->canceled, 1);
184 }
185 }
186
187 /* Adjust the delay based on processing time */
188 now = SDL_GetTicks();
189 interval = (now - tick);
190 if (interval > delay) {
191 delay = 0;
192 } else {
193 delay -= interval;
194 }
195
196 /* Note that each time a timer is added, this will return
197 immediately, but we process the timers added all at once.
198 That's okay, it just means we run through the loop a few
199 extra times.
200 */
201 SDL_SemWaitTimeout(data->sem, delay);
202 }
203 return 0;
204}
#define SDL_SemWaitTimeout
#define SDL_MUTEX_MAXWAIT
Definition: SDL_mutex.h:49
uint32_t Uint32
Definition: SDL_stdinc.h:203
static void SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
Definition: SDL_timer.c:80

References SDL_Timer::callback, SDL_Timer::canceled, SDL_Timer::interval, SDL_Timer::next, NULL, SDL_Timer::param, SDL_Timer::scheduled, SDL_AddTimerInternal(), SDL_AtomicGet, SDL_AtomicLock, SDL_AtomicSet, SDL_AtomicUnlock, SDL_GetTicks(), SDL_MUTEX_MAXWAIT, and SDL_SemWaitTimeout.

Referenced by SDL_TimerInit().

Variable Documentation

◆ SDL_timer_data

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().