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 "SDL_thread.h"
28 
29 /* #define DEBUG_TIMERS */
30 
31 typedef struct _SDL_Timer
32 {
33  int timerID;
35  void *param;
38  volatile SDL_bool canceled;
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;
63  SDL_Timer * volatile pending;
64  SDL_Timer * volatile freelist;
65  volatile SDL_bool active;
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 (!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 (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  current->canceled = SDL_TRUE;
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 (!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  data->active = SDL_TRUE;
224  /* !!! FIXME: this is nasty. */
225 #if defined(__WIN32__) && !defined(HAVE_LIBC)
226 #undef SDL_CreateThread
227 #if SDL_DYNAMIC_API
228  data->thread = SDL_CreateThread_REAL(SDL_TimerThread, name, data, NULL, NULL);
229 #else
230  data->thread = SDL_CreateThread(SDL_TimerThread, name, data, NULL, NULL);
231 #endif
232 #else
233  data->thread = SDL_CreateThread(SDL_TimerThread, name, data);
234 #endif
235  if (!data->thread) {
236  SDL_TimerQuit();
237  return -1;
238  }
239 
240  SDL_AtomicSet(&data->nextID, 1);
241  }
242  return 0;
243 }
244 
245 void
247 {
249  SDL_Timer *timer;
250  SDL_TimerMap *entry;
251 
252  if (data->active) {
253  data->active = SDL_FALSE;
254 
255  /* Shutdown the timer thread */
256  if (data->thread) {
257  SDL_SemPost(data->sem);
258  SDL_WaitThread(data->thread, NULL);
259  data->thread = NULL;
260  }
261 
262  SDL_DestroySemaphore(data->sem);
263  data->sem = NULL;
264 
265  /* Clean up the timer entries */
266  while (data->timers) {
267  timer = data->timers;
268  data->timers = timer->next;
269  SDL_free(timer);
270  }
271  while (data->freelist) {
272  timer = data->freelist;
273  data->freelist = timer->next;
274  SDL_free(timer);
275  }
276  while (data->timermap) {
277  entry = data->timermap;
278  data->timermap = entry->next;
279  SDL_free(entry);
280  }
281 
283  data->timermap_lock = NULL;
284  }
285 }
286 
289 {
291  SDL_Timer *timer;
292  SDL_TimerMap *entry;
293 
294  if (!data->active) {
295  int status = 0;
296 
297  SDL_AtomicLock(&data->lock);
298  if (!data->active) {
299  status = SDL_TimerInit();
300  }
301  SDL_AtomicUnlock(&data->lock);
302 
303  if (status < 0) {
304  return 0;
305  }
306  }
307 
308  SDL_AtomicLock(&data->lock);
309  timer = data->freelist;
310  if (timer) {
311  data->freelist = timer->next;
312  }
313  SDL_AtomicUnlock(&data->lock);
314 
315  if (timer) {
316  SDL_RemoveTimer(timer->timerID);
317  } else {
318  timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
319  if (!timer) {
320  SDL_OutOfMemory();
321  return 0;
322  }
323  }
324  timer->timerID = SDL_AtomicIncRef(&data->nextID);
325  timer->callback = callback;
326  timer->param = param;
327  timer->interval = interval;
328  timer->scheduled = SDL_GetTicks() + interval;
329  timer->canceled = SDL_FALSE;
330 
331  entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
332  if (!entry) {
333  SDL_free(timer);
334  SDL_OutOfMemory();
335  return 0;
336  }
337  entry->timer = timer;
338  entry->timerID = timer->timerID;
339 
341  entry->next = data->timermap;
342  data->timermap = entry;
344 
345  /* Add the timer to the pending list for the timer thread */
346  SDL_AtomicLock(&data->lock);
347  timer->next = data->pending;
348  data->pending = timer;
349  SDL_AtomicUnlock(&data->lock);
350 
351  /* Wake up the timer thread if necessary */
352  SDL_SemPost(data->sem);
353 
354  return entry->timerID;
355 }
356 
357 SDL_bool
359 {
361  SDL_TimerMap *prev, *entry;
362  SDL_bool canceled = SDL_FALSE;
363 
364  /* Find the timer */
366  prev = NULL;
367  for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
368  if (entry->timerID == id) {
369  if (prev) {
370  prev->next = entry->next;
371  } else {
372  data->timermap = entry->next;
373  }
374  break;
375  }
376  }
378 
379  if (entry) {
380  if (!entry->timer->canceled) {
381  entry->timer->canceled = SDL_TRUE;
382  canceled = SDL_TRUE;
383  }
384  SDL_free(entry);
385  }
386  return canceled;
387 }
388 
389 /* 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:246
SDL_Timer * timer
Definition: SDL_timer.c:45
volatile SDL_bool canceled
Definition: SDL_timer.c:38
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:155
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
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:288
GLuint const GLchar * name
SDL_atomic_t nextID
Definition: SDL_timer.c:53
volatile SDL_bool active
Definition: SDL_timer.c:65
GLfloat param
#define SDL_SemPost
#define SDL_AtomicUnlock
SDL_bool
Definition: SDL_stdinc.h:126
SDL_bool SDL_RemoveTimer(SDL_TimerID id)
Remove a timer knowing its ID.
Definition: SDL_timer.c:358
SDL_Timer *volatile freelist
Definition: SDL_timer.c:64
#define SDL_SemWaitTimeout
struct _SDL_Timer * next
Definition: SDL_timer.c:39
#define SDL_MUTEX_MAXWAIT
Definition: SDL_mutex.h:49
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:151
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
#define SDL_CreateThread
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
static void SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
Definition: SDL_timer.c:80
SDL_mutex * timermap_lock
Definition: SDL_timer.c:55
#define SDL_DestroyMutex
SDL_Timer *volatile pending
Definition: SDL_timer.c:63
#define SDL_DestroySemaphore
static SDL_TimerData SDL_timer_data
Definition: SDL_timer.c:71
#define SDL_AtomicSet
#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