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) {
304  SDL_OutOfMemory();
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);
318  SDL_OutOfMemory();
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 }

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 }

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 }

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) {
229  SDL_TimerQuit();
230  return -1;
231  }
232 
233  SDL_AtomicSet(&data->nextID, 1);
234  }
235  return 0;
236 }

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 }

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 }

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

Sint32
int32_t Sint32
Definition: SDL_stdinc.h:197
SDL_TimerQuit
void SDL_TimerQuit(void)
Definition: SDL_timer.c:239
SDL_CreateSemaphore
#define SDL_CreateSemaphore
Definition: SDL_dynapi_overrides.h:264
SDL_AtomicCAS
#define SDL_AtomicCAS
Definition: SDL_dynapi_overrides.h:66
SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:260
NULL
#define NULL
Definition: begin_code.h:164
SDL_AtomicLock
#define SDL_AtomicLock
Definition: SDL_dynapi_overrides.h:64
SDL_TimerInit
int SDL_TimerInit(void)
Definition: SDL_timer.c:207
SDL_TimerMap
Definition: SDL_timer.c:43
SDL_AtomicIncRef
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:234
SDL_CreateMutex
#define SDL_CreateMutex
Definition: SDL_dynapi_overrides.h:259
SDL_TimerMap::timer
SDL_Timer * timer
Definition: SDL_timer.c:45
callback
static Uint32 callback(Uint32 interval, void *param)
Definition: testtimer.c:34
SDL_Timer::next
struct _SDL_Timer * next
Definition: SDL_timer.c:39
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
SDL_Timer
Definition: SDL_timer.c:32
SDL_Timer::param
void * param
Definition: SDL_timer.c:35
SDL_SemPost
#define SDL_SemPost
Definition: SDL_dynapi_overrides.h:269
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_SemWaitTimeout
#define SDL_SemWaitTimeout
Definition: SDL_dynapi_overrides.h:268
SDL_TimerMap::next
struct _SDL_TimerMap * next
Definition: SDL_timer.c:46
SDL_AtomicUnlock
#define SDL_AtomicUnlock
Definition: SDL_dynapi_overrides.h:65
SDL_Timer::timerID
int timerID
Definition: SDL_timer.c:33
SDL_CreateThreadInternal
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
SDL_RemoveTimer
SDL_bool SDL_RemoveTimer(SDL_TimerID id)
Remove a timer knowing its ID.
Definition: SDL_timer.c:342
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
name
GLuint const GLchar * name
Definition: SDL_opengl_glext.h:660
SDL_MUTEX_MAXWAIT
#define SDL_MUTEX_MAXWAIT
Definition: SDL_mutex.h:49
SDL_GetTicks
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
param
GLfloat param
Definition: SDL_opengl_glext.h:370
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_Timer::callback
SDL_TimerCallback callback
Definition: SDL_timer.c:34
SDL_TimerMap::timerID
int timerID
Definition: SDL_timer.c:44
SDL_AddTimerInternal
static void SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
Definition: SDL_timer.c:80
SDL_Timer::interval
Uint32 interval
Definition: SDL_timer.c:36
SDL_TimerData
Definition: SDL_timer.c:50
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_TimerData::timermap_lock
SDL_mutex * timermap_lock
Definition: SDL_timer.c:55
SDL_DestroyMutex
#define SDL_DestroyMutex
Definition: SDL_dynapi_overrides.h:263
SDL_timer_data
static SDL_TimerData SDL_timer_data
Definition: SDL_timer.c:71
SDL_DestroySemaphore
#define SDL_DestroySemaphore
Definition: SDL_dynapi_overrides.h:265
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:162
SDL_Timer::scheduled
Uint32 scheduled
Definition: SDL_timer.c:37
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_AtomicSet
#define SDL_AtomicSet
Definition: SDL_dynapi_overrides.h:67
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:68
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:262
SDL_Timer::canceled
SDL_atomic_t canceled
Definition: SDL_timer.c:38
SDL_WaitThread
#define SDL_WaitThread
Definition: SDL_dynapi_overrides.h:478
SDL_TimerThread
static int SDL_TimerThread(void *_data)
Definition: SDL_timer.c:101