zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDL_timer.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2013 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_config.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;
34  SDL_TimerCallback callback;
35  void *param;
36  Uint32 interval;
37  Uint32 scheduled;
38  volatile SDL_bool canceled;
39  struct _SDL_Timer *next;
40 } SDL_Timer;
41 
42 typedef struct _SDL_TimerMap
43 {
44  int timerID;
45  SDL_Timer *timer;
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 */
53  SDL_atomic_t nextID;
54  SDL_TimerMap *timermap;
55  SDL_mutex *timermap_lock;
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 */
68  SDL_Timer *timers;
69 } SDL_TimerData;
70 
71 static SDL_TimerData SDL_timer_data;
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
80 SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
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 {
208  SDL_TimerData *data = &SDL_timer_data;
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) {
219  SDL_DestroyMutex(data->timermap_lock);
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  data->thread = SDL_CreateThread(SDL_TimerThread, name, data, NULL, NULL);
228 #else
229  data->thread = SDL_CreateThread(SDL_TimerThread, name, data);
230 #endif
231  if (!data->thread) {
232  SDL_TimerQuit();
233  return -1;
234  }
235 
236  SDL_AtomicSet(&data->nextID, 1);
237  }
238  return 0;
239 }
240 
241 void
243 {
244  SDL_TimerData *data = &SDL_timer_data;
245  SDL_Timer *timer;
246  SDL_TimerMap *entry;
247 
248  if (data->active) {
249  data->active = SDL_FALSE;
250 
251  /* Shutdown the timer thread */
252  if (data->thread) {
253  SDL_SemPost(data->sem);
254  SDL_WaitThread(data->thread, NULL);
255  data->thread = NULL;
256  }
257 
258  SDL_DestroySemaphore(data->sem);
259  data->sem = NULL;
260 
261  /* Clean up the timer entries */
262  while (data->timers) {
263  timer = data->timers;
264  data->timers = timer->next;
265  SDL_free(timer);
266  }
267  while (data->freelist) {
268  timer = data->freelist;
269  data->freelist = timer->next;
270  SDL_free(timer);
271  }
272  while (data->timermap) {
273  entry = data->timermap;
274  data->timermap = entry->next;
275  SDL_free(entry);
276  }
277 
278  SDL_DestroyMutex(data->timermap_lock);
279  data->timermap_lock = NULL;
280  }
281 }
282 
284 SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
285 {
286  SDL_TimerData *data = &SDL_timer_data;
287  SDL_Timer *timer;
288  SDL_TimerMap *entry;
289 
290  if (!data->active) {
291  int status = 0;
292 
293  SDL_AtomicLock(&data->lock);
294  if (!data->active) {
295  status = SDL_TimerInit();
296  }
297  SDL_AtomicUnlock(&data->lock);
298 
299  if (status < 0) {
300  return 0;
301  }
302  }
303 
304  SDL_AtomicLock(&data->lock);
305  timer = data->freelist;
306  if (timer) {
307  data->freelist = timer->next;
308  }
309  SDL_AtomicUnlock(&data->lock);
310 
311  if (timer) {
312  SDL_RemoveTimer(timer->timerID);
313  } else {
314  timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
315  if (!timer) {
316  SDL_OutOfMemory();
317  return 0;
318  }
319  }
320  timer->timerID = SDL_AtomicIncRef(&data->nextID);
321  timer->callback = callback;
322  timer->param = param;
323  timer->interval = interval;
324  timer->scheduled = SDL_GetTicks() + interval;
325  timer->canceled = SDL_FALSE;
326 
327  entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
328  if (!entry) {
329  SDL_free(timer);
330  SDL_OutOfMemory();
331  return 0;
332  }
333  entry->timer = timer;
334  entry->timerID = timer->timerID;
335 
336  SDL_LockMutex(data->timermap_lock);
337  entry->next = data->timermap;
338  data->timermap = entry;
339  SDL_UnlockMutex(data->timermap_lock);
340 
341  /* Add the timer to the pending list for the timer thread */
342  SDL_AtomicLock(&data->lock);
343  timer->next = data->pending;
344  data->pending = timer;
345  SDL_AtomicUnlock(&data->lock);
346 
347  /* Wake up the timer thread if necessary */
348  SDL_SemPost(data->sem);
349 
350  return entry->timerID;
351 }
352 
353 SDL_bool
355 {
356  SDL_TimerData *data = &SDL_timer_data;
357  SDL_TimerMap *prev, *entry;
358  SDL_bool canceled = SDL_FALSE;
359 
360  /* Find the timer */
361  SDL_LockMutex(data->timermap_lock);
362  prev = NULL;
363  for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
364  if (entry->timerID == id) {
365  if (prev) {
366  prev->next = entry->next;
367  } else {
368  data->timermap = entry->next;
369  }
370  break;
371  }
372  }
373  SDL_UnlockMutex(data->timermap_lock);
374 
375  if (entry) {
376  if (!entry->timer->canceled) {
377  entry->timer->canceled = SDL_TRUE;
378  canceled = SDL_TRUE;
379  }
380  SDL_free(entry);
381  }
382  return canceled;
383 }
384 
385 /* vi: set ts=4 sw=4 expandtab: */
DECLSPEC SDL_sem *SDLCALL SDL_CreateSemaphore(Uint32 initial_value)
Definition: SDL_syssem.c:85
static int SDL_TimerThread(void *_data)
Definition: SDL_timer.c:101
#define SDL_MUTEX_MAXWAIT
Definition: SDL_mutex.h:49
volatile SDL_bool active
Definition: SDL_events.c:76
GLenum GLint param
Definition: gl2ext.h:1491
int32_t Sint32
A signed 32-bit integer type.
Definition: SDL_stdinc.h:141
DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock)
Unlock a spin lock by setting it to 0. Always returns immediately.
Definition: SDL_spinlock.c:109
struct SDL_semaphore SDL_sem
Definition: SDL_mutex.h:107
#define NULL
Definition: ftobjs.h:61
DECLSPEC int SDLCALL SDL_SemWaitTimeout(SDL_sem *sem, Uint32 ms)
Definition: SDL_syssem.c:150
A type representing an atomic integer value. It is a struct so people don&#39;t accidentally use numeric ...
Definition: SDL_atomic.h:233
DECLSPEC int SDLCALL SDL_LockMutex(SDL_mutex *mutex)
Definition: SDL_sysmutex.c:73
SDL_bool
Definition: SDL_stdinc.h:116
DECLSPEC SDL_mutex *SDLCALL SDL_CreateMutex(void)
Definition: SDL_sysmutex.c:38
DECLSPEC void SDLCALL SDL_free(void *mem)
EGLImageKHR EGLint * name
Definition: eglext.h:284
void SDL_TimerQuit(void)
Definition: SDL_timer.c:242
DECLSPEC int SDLCALL SDL_UnlockMutex(SDL_mutex *mutex)
Definition: SDL_sysmutex.c:160
DECLSPEC SDL_TimerID SDLCALL SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
Add a new timer to the pool of timers already running.
Definition: SDL_timer.c:284
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:145
DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread *thread, int *status)
Definition: SDL_thread.c:399
DECLSPEC Uint32 SDLCALL SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Definition: SDL_systimer.c:44
DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock)
Lock a spin lock by setting it to a non-zero value.
Definition: SDL_spinlock.c:100
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl2ext.h:848
struct _SDL_TimerMap SDL_TimerMap
#define SDL_CACHELINE_SIZE
Definition: SDL_cpuinfo.h:77
struct _SDL_Timer SDL_Timer
int SDL_TimerID
Definition: SDL_timer.h:75
DECLSPEC void *SDLCALL SDL_malloc(size_t size)
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:297
DECLSPEC void SDLCALL SDL_DestroySemaphore(SDL_sem *sem)
Definition: SDL_syssem.c:111
DECLSPEC SDL_Thread *SDLCALL SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data)
static SDL_Thread * thread
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
static void SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
Definition: SDL_timer.c:80
struct SDL_mutex SDL_mutex
Definition: SDL_mutex.h:59
SDL_mutex * lock
Definition: SDL_events.c:75
static SDL_TimerData SDL_timer_data
Definition: SDL_timer.c:71
SDL_FORCE_INLINE int SDL_AtomicSet(SDL_atomic_t *a, int v)
Set an atomic variable to a value.
Definition: SDL_atomic.h:253
DECLSPEC SDL_bool SDLCALL SDL_RemoveTimer(SDL_TimerID id)
Remove a timer knowing its ID.
Definition: SDL_timer.c:354
DECLSPEC void SDLCALL SDL_DestroyMutex(SDL_mutex *mutex)
Definition: SDL_sysmutex.c:61
int SDL_SpinLock
Definition: SDL_atomic.h:96
DECLSPEC int SDLCALL SDL_SemPost(SDL_sem *sem)
Definition: SDL_syssem.c:200
int SDL_TimerInit(void)
Definition: SDL_timer.c:206