Patched to compile on C89 compilers.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../SDL_internal.h"
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"
29 /* #define DEBUG_TIMERS */
31 typedef struct _SDL_Timer
34 SDL_TimerCallback callback;
38 volatile SDL_bool canceled;
39 struct _SDL_Timer *next;
42 typedef struct _SDL_TimerMap
46 struct _SDL_TimerMap *next;
49 /* The timers are kept in a sorted list */
51 /* Data used by the main thread */
54 SDL_TimerMap *timermap;
55 SDL_mutex *timermap_lock;
57 /* Padding to separate cache lines between threads */
58 char cache_pad[SDL_CACHELINE_SIZE];
60 /* Data used to communicate with the timer thread */
63 SDL_Timer * volatile pending;
64 SDL_Timer * volatile freelist;
65 volatile SDL_bool active;
67 /* List of timers - this is only touched by the timer thread */
71 static SDL_TimerData SDL_timer_data;
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.
76 * Timers are removed by simply setting a canceled flag
80 SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
82 SDL_Timer *prev, *curr;
85 for (curr = data->timers; curr; prev = curr, curr = curr->next) {
86 if ((Sint32)(timer->scheduled-curr->scheduled) < 0) {
91 /* Insert the timer here! */
101 SDL_TimerThread(void *_data)
103 SDL_TimerData *data = (SDL_TimerData *)_data;
106 SDL_Timer *freelist_head = NULL;
107 SDL_Timer *freelist_tail = NULL;
108 Uint32 tick, now, interval, delay;
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
116 /* Pending and freelist maintenance */
117 SDL_AtomicLock(&data->lock);
119 /* Get any timers ready to be queued */
120 pending = data->pending;
121 data->pending = NULL;
123 /* Make any unused timer structures available */
125 freelist_tail->next = data->freelist;
126 data->freelist = freelist_head;
129 SDL_AtomicUnlock(&data->lock);
131 /* Sort the pending timers into our list */
134 pending = pending->next;
135 SDL_AddTimerInternal(data, current);
137 freelist_head = NULL;
138 freelist_tail = NULL;
140 /* Check to see if we're still running, after maintenance */
145 /* Initial delay if there are no timers */
146 delay = SDL_MUTEX_MAXWAIT;
148 tick = SDL_GetTicks();
150 /* Process all the pending timers for this tick */
151 while (data->timers) {
152 current = data->timers;
154 if ((Sint32)(tick-current->scheduled) < 0) {
155 /* Scheduled for the future, wait a bit */
156 delay = (current->scheduled - tick);
160 /* We're going to do something with this timer */
161 data->timers = current->next;
163 if (current->canceled) {
166 interval = current->callback(current->interval, current->param);
170 /* Reschedule this timer */
171 current->scheduled = tick + interval;
172 SDL_AddTimerInternal(data, current);
174 if (!freelist_head) {
175 freelist_head = current;
178 freelist_tail->next = current;
180 freelist_tail = current;
182 current->canceled = SDL_TRUE;
186 /* Adjust the delay based on processing time */
187 now = SDL_GetTicks();
188 interval = (now - tick);
189 if (interval > delay) {
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
200 SDL_SemWaitTimeout(data->sem, delay);
208 SDL_TimerData *data = &SDL_timer_data;
211 const char *name = "SDLTimer";
212 data->timermap_lock = SDL_CreateMutex();
213 if (!data->timermap_lock) {
217 data->sem = SDL_CreateSemaphore(0);
219 SDL_DestroyMutex(data->timermap_lock);
223 data->active = SDL_TRUE;
224 /* !!! FIXME: this is nasty. */
225 #if defined(__WIN32__) && !defined(HAVE_LIBC)
226 #undef SDL_CreateThread
228 data->thread = SDL_CreateThread_REAL(SDL_TimerThread, name, data, NULL, NULL);
230 data->thread = SDL_CreateThread(SDL_TimerThread, name, data, NULL, NULL);
233 data->thread = SDL_CreateThread(SDL_TimerThread, name, data);
240 SDL_AtomicSet(&data->nextID, 1);
248 SDL_TimerData *data = &SDL_timer_data;
253 data->active = SDL_FALSE;
255 /* Shutdown the timer thread */
257 SDL_SemPost(data->sem);
258 SDL_WaitThread(data->thread, NULL);
262 SDL_DestroySemaphore(data->sem);
265 /* Clean up the timer entries */
266 while (data->timers) {
267 timer = data->timers;
268 data->timers = timer->next;
271 while (data->freelist) {
272 timer = data->freelist;
273 data->freelist = timer->next;
276 while (data->timermap) {
277 entry = data->timermap;
278 data->timermap = entry->next;
282 SDL_DestroyMutex(data->timermap_lock);
283 data->timermap_lock = NULL;
288 SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *param)
290 SDL_TimerData *data = &SDL_timer_data;
297 SDL_AtomicLock(&data->lock);
299 status = SDL_TimerInit();
301 SDL_AtomicUnlock(&data->lock);
308 SDL_AtomicLock(&data->lock);
309 timer = data->freelist;
311 data->freelist = timer->next;
313 SDL_AtomicUnlock(&data->lock);
316 SDL_RemoveTimer(timer->timerID);
318 timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
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;
331 entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
337 entry->timer = timer;
338 entry->timerID = timer->timerID;
340 SDL_LockMutex(data->timermap_lock);
341 entry->next = data->timermap;
342 data->timermap = entry;
343 SDL_UnlockMutex(data->timermap_lock);
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);
351 /* Wake up the timer thread if necessary */
352 SDL_SemPost(data->sem);
354 return entry->timerID;
358 SDL_RemoveTimer(SDL_TimerID id)
360 SDL_TimerData *data = &SDL_timer_data;
361 SDL_TimerMap *prev, *entry;
362 SDL_bool canceled = SDL_FALSE;
365 SDL_LockMutex(data->timermap_lock);
367 for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
368 if (entry->timerID == id) {
370 prev->next = entry->next;
372 data->timermap = entry->next;
377 SDL_UnlockMutex(data->timermap_lock);
380 if (!entry->timer->canceled) {
381 entry->timer->canceled = SDL_TRUE;
389 /* vi: set ts=4 sw=4 expandtab: */