SDL  2.0
SDL_timer.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../SDL_internal.h"
22 
23 #include "SDL_timer.h"
24 #include "SDL_timer_c.h"
25 #include "SDL_atomic.h"
26 #include "SDL_cpuinfo.h"
27 #include "../thread/SDL_systhread.h"
28 
29 /* #define DEBUG_TIMERS */
30 
31 typedef struct _SDL_Timer
32 {
33  int timerID;
35  void *param;
39  struct _SDL_Timer *next;
40 } SDL_Timer;
41 
42 typedef struct _SDL_TimerMap
43 {
44  int timerID;
46  struct _SDL_TimerMap *next;
47 } SDL_TimerMap;
48 
49 /* The timers are kept in a sorted list */
50 typedef struct {
51  /* Data used by the main thread */
56 
57  /* Padding to separate cache lines between threads */
58  char cache_pad[SDL_CACHELINE_SIZE];
59 
60  /* Data used to communicate with the timer thread */
62  SDL_sem *sem;
66 
67  /* List of timers - this is only touched by the timer thread */
70 
72 
73 /* The idea here is that any thread might add a timer, but a single
74  * thread manages the active timer queue, sorted by scheduling time.
75  *
76  * Timers are removed by simply setting a canceled flag
77  */
78 
79 static void
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 }
99 
100 static int
101 SDL_TimerThread(void *_data)
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->scheduled = tick + interval;
172  SDL_AddTimerInternal(data, current);
173  } else {
174  if (!freelist_head) {
175  freelist_head = current;
176  }
177  if (freelist_tail) {
178  freelist_tail->next = current;
179  }
180  freelist_tail = current;
181 
182  SDL_AtomicSet(&current->canceled, 1);
183  }
184  }
185 
186  /* Adjust the delay based on processing time */
187  now = SDL_GetTicks();
188  interval = (now - tick);
189  if (interval > delay) {
190  delay = 0;
191  } else {
192  delay -= interval;
193  }
194 
195  /* Note that each time a timer is added, this will return
196  immediately, but we process the timers added all at once.
197  That's okay, it just means we run through the loop a few
198  extra times.
199  */
200  SDL_SemWaitTimeout(data->sem, delay);
201  }
202  return 0;
203 }
204 
205 int
207 {
209 
210  if (!SDL_AtomicGet(&data->active)) {
211  const char *name = "SDLTimer";
212  data->timermap_lock = SDL_CreateMutex();
213  if (!data->timermap_lock) {
214  return -1;
215  }
216 
217  data->sem = SDL_CreateSemaphore(0);
218  if (!data->sem) {
220  return -1;
221  }
222 
223  SDL_AtomicSet(&data->active, 1);
224 
225  /* Timer threads use a callback into the app, so we can't set a limited stack size here. */
226  data->thread = SDL_CreateThreadInternal(SDL_TimerThread, name, 0, data);
227  if (!data->thread) {
228  SDL_TimerQuit();
229  return -1;
230  }
231 
232  SDL_AtomicSet(&data->nextID, 1);
233  }
234  return 0;
235 }
236 
237 void
239 {
241  SDL_Timer *timer;
242  SDL_TimerMap *entry;
243 
244  if (SDL_AtomicCAS(&data->active, 1, 0)) { /* active? Move to inactive. */
245  /* Shutdown the timer thread */
246  if (data->thread) {
247  SDL_SemPost(data->sem);
248  SDL_WaitThread(data->thread, NULL);
249  data->thread = NULL;
250  }
251 
252  SDL_DestroySemaphore(data->sem);
253  data->sem = NULL;
254 
255  /* Clean up the timer entries */
256  while (data->timers) {
257  timer = data->timers;
258  data->timers = timer->next;
259  SDL_free(timer);
260  }
261  while (data->freelist) {
262  timer = data->freelist;
263  data->freelist = timer->next;
264  SDL_free(timer);
265  }
266  while (data->timermap) {
267  entry = data->timermap;
268  data->timermap = entry->next;
269  SDL_free(entry);
270  }
271 
273  data->timermap_lock = NULL;
274  }
275 }
276 
279 {
281  SDL_Timer *timer;
282  SDL_TimerMap *entry;
283 
284  SDL_AtomicLock(&data->lock);
285  if (!SDL_AtomicGet(&data->active)) {
286  if (SDL_TimerInit() < 0) {
287  SDL_AtomicUnlock(&data->lock);
288  return 0;
289  }
290  }
291 
292  timer = data->freelist;
293  if (timer) {
294  data->freelist = timer->next;
295  }
296  SDL_AtomicUnlock(&data->lock);
297 
298  if (timer) {
299  SDL_RemoveTimer(timer->timerID);
300  } else {
301  timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
302  if (!timer) {
303  SDL_OutOfMemory();
304  return 0;
305  }
306  }
307  timer->timerID = SDL_AtomicIncRef(&data->nextID);
308  timer->callback = callback;
309  timer->param = param;
310  timer->interval = interval;
311  timer->scheduled = SDL_GetTicks() + interval;
312  SDL_AtomicSet(&timer->canceled, 0);
313 
314  entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
315  if (!entry) {
316  SDL_free(timer);
317  SDL_OutOfMemory();
318  return 0;
319  }
320  entry->timer = timer;
321  entry->timerID = timer->timerID;
322 
324  entry->next = data->timermap;
325  data->timermap = entry;
327 
328  /* Add the timer to the pending list for the timer thread */
329  SDL_AtomicLock(&data->lock);
330  timer->next = data->pending;
331  data->pending = timer;
332  SDL_AtomicUnlock(&data->lock);
333 
334  /* Wake up the timer thread if necessary */
335  SDL_SemPost(data->sem);
336 
337  return entry->timerID;
338 }
339 
340 SDL_bool
342 {
344  SDL_TimerMap *prev, *entry;
345  SDL_bool canceled = SDL_FALSE;
346 
347  /* Find the timer */
349  prev = NULL;
350  for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
351  if (entry->timerID == id) {
352  if (prev) {
353  prev->next = entry->next;
354  } else {
355  data->timermap = entry->next;
356  }
357  break;
358  }
359  }
361 
362  if (entry) {
363  if (!SDL_AtomicGet(&entry->timer->canceled)) {
364  SDL_AtomicSet(&entry->timer->canceled, 1);
365  canceled = SDL_TRUE;
366  }
367  SDL_free(entry);
368  }
369  return canceled;
370 }
371 
372 /* vi: set ts=4 sw=4 expandtab: */
static int SDL_TimerThread(void *_data)
Definition: SDL_timer.c:101
#define SDL_LockMutex
void SDL_TimerQuit(void)
Definition: SDL_timer.c:238
SDL_Timer * timer
Definition: SDL_timer.c:45
SDL_TimerMap * timermap
Definition: SDL_timer.c:54
#define SDL_AtomicLock
Uint32(* SDL_TimerCallback)(Uint32 interval, void *param)
Definition: SDL_timer.h:81
A type representing an atomic integer value. It is a struct so people don&#39;t accidentally use numeric ...
Definition: SDL_atomic.h:189
#define SDL_AtomicCAS
Uint32 interval
Definition: SDL_timer.c:36
#define SDL_CreateSemaphore
#define SDL_CreateMutex
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
int SDL_TimerInit(void)
Definition: SDL_timer.c:206
SDL_Thread * thread
Definition: SDL_timer.c:52
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
Add a new timer to the pool of timers already running.
Definition: SDL_timer.c:278
GLuint const GLchar * name
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:159
SDL_atomic_t nextID
Definition: SDL_timer.c:53
GLfloat param
#define SDL_SemPost
#define SDL_AtomicUnlock
SDL_bool SDL_RemoveTimer(SDL_TimerID id)
Remove a timer knowing its ID.
Definition: SDL_timer.c:341
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:427
#define SDL_SemWaitTimeout
struct _SDL_Timer * next
Definition: SDL_timer.c:39
#define SDL_MUTEX_MAXWAIT
Definition: SDL_mutex.h:49
SDL_atomic_t active
Definition: SDL_timer.c:65
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Uint32 scheduled
Definition: SDL_timer.c:37
void SDL_free(void *mem)
SDL_TimerCallback callback
Definition: SDL_timer.c:34
int timerID
Definition: SDL_timer.c:33
#define SDL_CACHELINE_SIZE
Definition: SDL_cpuinfo.h:77
static Uint32 callback(Uint32 interval, void *param)
Definition: testtimer.c:34
int32_t Sint32
A signed 32-bit integer type.
Definition: SDL_stdinc.h:155
SDL_sem * sem
Definition: SDL_timer.c:62
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:225
struct _SDL_TimerMap * next
Definition: SDL_timer.c:46
#define NULL
Definition: begin_code.h:143
void * param
Definition: SDL_timer.c:35
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:130
static void SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
Definition: SDL_timer.c:80
SDL_Timer * freelist
Definition: SDL_timer.c:64
SDL_mutex * timermap_lock
Definition: SDL_timer.c:55
SDL_Timer * pending
Definition: SDL_timer.c:63
#define SDL_DestroyMutex
#define SDL_DestroySemaphore
static SDL_TimerData SDL_timer_data
Definition: SDL_timer.c:71
#define SDL_AtomicSet
SDL_atomic_t canceled
Definition: SDL_timer.c:38
#define SDL_AtomicGet
#define SDL_UnlockMutex
#define SDL_malloc
SDL_SpinLock lock
Definition: SDL_timer.c:61
int SDL_SpinLock
Definition: SDL_atomic.h:89
SDL_Timer * timers
Definition: SDL_timer.c:68
int SDL_TimerID
Definition: SDL_timer.h:86
#define SDL_WaitThread