1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 #include "SDL_timer.h"
25 #include "SDL_timer_c.h"
26 #include "SDL_mutex.h"
27 #include "SDL_systimer.h"
28
29 /* #define DEBUG_TIMERS */
30
31 int SDL_timer_started = 0;
32 int SDL_timer_running = 0;
33
34 /* Data to handle a single periodic alarm */
35 Uint32 SDL_alarm_interval = 0;
36 SDL_TimerCallback SDL_alarm_callback;
37
38 /* Data used for a thread-based timer */
39 static int SDL_timer_threaded = 0;
40
41 struct _SDL_TimerID {
42 Uint32 interval;
43 SDL_NewTimerCallback cb;
44 void *param;
45 Uint32 last_alarm;
46 struct _SDL_TimerID *next;
47 };
48
49 static SDL_TimerID SDL_timers = NULL;
50 static SDL_mutex *SDL_timer_mutex;
51 static volatile SDL_bool list_changed = SDL_FALSE;
52
53 /* Set whether or not the timer should use a thread.
54 This should not be called while the timer subsystem is running.
55 */
SDL_SetTimerThreaded(int value)56 int SDL_SetTimerThreaded(int value)
57 {
58 int retval;
59
60 if ( SDL_timer_started ) {
61 SDL_SetError("Timer already initialized");
62 retval = -1;
63 } else {
64 retval = 0;
65 SDL_timer_threaded = value;
66 }
67 return retval;
68 }
69
SDL_TimerInit(void)70 int SDL_TimerInit(void)
71 {
72 int retval;
73
74 retval = 0;
75 if ( SDL_timer_started ) {
76 SDL_TimerQuit();
77 }
78 if ( ! SDL_timer_threaded ) {
79 retval = SDL_SYS_TimerInit();
80 }
81 if ( SDL_timer_threaded ) {
82 SDL_timer_mutex = SDL_CreateMutex();
83 }
84 if ( retval == 0 ) {
85 SDL_timer_started = 1;
86 }
87 return(retval);
88 }
89
SDL_TimerQuit(void)90 void SDL_TimerQuit(void)
91 {
92 SDL_SetTimer(0, NULL);
93 if ( SDL_timer_threaded < 2 ) {
94 SDL_SYS_TimerQuit();
95 }
96 if ( SDL_timer_threaded ) {
97 SDL_DestroyMutex(SDL_timer_mutex);
98 SDL_timer_mutex = NULL;
99 }
100 SDL_timer_started = 0;
101 SDL_timer_threaded = 0;
102 }
103
SDL_ThreadedTimerCheck(void)104 void SDL_ThreadedTimerCheck(void)
105 {
106 Uint32 now, ms;
107 SDL_TimerID t, prev, next;
108 SDL_bool removed;
109
110 SDL_mutexP(SDL_timer_mutex);
111 list_changed = SDL_FALSE;
112 now = SDL_GetTicks();
113 for ( prev = NULL, t = SDL_timers; t; t = next ) {
114 removed = SDL_FALSE;
115 ms = t->interval - SDL_TIMESLICE;
116 next = t->next;
117 if ( (int)(now - t->last_alarm) > (int)ms ) {
118 struct _SDL_TimerID timer;
119
120 if ( (now - t->last_alarm) < t->interval ) {
121 t->last_alarm += t->interval;
122 } else {
123 t->last_alarm = now;
124 }
125 #ifdef DEBUG_TIMERS
126 printf("Executing timer %p (thread = %d)\n",
127 t, SDL_ThreadID());
128 #endif
129 timer = *t;
130 SDL_mutexV(SDL_timer_mutex);
131 ms = timer.cb(timer.interval, timer.param);
132 SDL_mutexP(SDL_timer_mutex);
133 if ( list_changed ) {
134 /* Abort, list of timers modified */
135 /* FIXME: what if ms was changed? */
136 break;
137 }
138 if ( ms != t->interval ) {
139 if ( ms ) {
140 t->interval = ROUND_RESOLUTION(ms);
141 } else {
142 /* Remove timer from the list */
143 #ifdef DEBUG_TIMERS
144 printf("SDL: Removing timer %p\n", t);
145 #endif
146 if ( prev ) {
147 prev->next = next;
148 } else {
149 SDL_timers = next;
150 }
151 SDL_free(t);
152 --SDL_timer_running;
153 removed = SDL_TRUE;
154 }
155 }
156 }
157 /* Don't update prev if the timer has disappeared */
158 if ( ! removed ) {
159 prev = t;
160 }
161 }
162 SDL_mutexV(SDL_timer_mutex);
163 }
164
SDL_AddTimerInternal(Uint32 interval,SDL_NewTimerCallback callback,void * param)165 static SDL_TimerID SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback, void *param)
166 {
167 SDL_TimerID t;
168 t = (SDL_TimerID) SDL_malloc(sizeof(struct _SDL_TimerID));
169 if ( t ) {
170 t->interval = ROUND_RESOLUTION(interval);
171 t->cb = callback;
172 t->param = param;
173 t->last_alarm = SDL_GetTicks();
174 t->next = SDL_timers;
175 SDL_timers = t;
176 ++SDL_timer_running;
177 list_changed = SDL_TRUE;
178 }
179 #ifdef DEBUG_TIMERS
180 printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32)t, SDL_timer_running);
181 #endif
182 return t;
183 }
184
SDL_AddTimer(Uint32 interval,SDL_NewTimerCallback callback,void * param)185 SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)
186 {
187 SDL_TimerID t;
188 if ( ! SDL_timer_mutex ) {
189 if ( SDL_timer_started ) {
190 SDL_SetError("This platform doesn't support multiple timers");
191 } else {
192 SDL_SetError("You must call SDL_Init(SDL_INIT_TIMER) first");
193 }
194 return NULL;
195 }
196 if ( ! SDL_timer_threaded ) {
197 SDL_SetError("Multiple timers require threaded events!");
198 return NULL;
199 }
200 SDL_mutexP(SDL_timer_mutex);
201 t = SDL_AddTimerInternal(interval, callback, param);
202 SDL_mutexV(SDL_timer_mutex);
203 return t;
204 }
205
SDL_RemoveTimer(SDL_TimerID id)206 SDL_bool SDL_RemoveTimer(SDL_TimerID id)
207 {
208 SDL_TimerID t, prev = NULL;
209 SDL_bool removed;
210
211 removed = SDL_FALSE;
212 SDL_mutexP(SDL_timer_mutex);
213 /* Look for id in the linked list of timers */
214 for (t = SDL_timers; t; prev=t, t = t->next ) {
215 if ( t == id ) {
216 if(prev) {
217 prev->next = t->next;
218 } else {
219 SDL_timers = t->next;
220 }
221 SDL_free(t);
222 --SDL_timer_running;
223 removed = SDL_TRUE;
224 list_changed = SDL_TRUE;
225 break;
226 }
227 }
228 #ifdef DEBUG_TIMERS
229 printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, SDL_timer_running, SDL_ThreadID());
230 #endif
231 SDL_mutexV(SDL_timer_mutex);
232 return removed;
233 }
234
235 /* Old style callback functions are wrapped through this */
callback_wrapper(Uint32 ms,void * param)236 static Uint32 SDLCALL callback_wrapper(Uint32 ms, void *param)
237 {
238 SDL_TimerCallback func = (SDL_TimerCallback) param;
239 return (*func)(ms);
240 }
241
SDL_SetTimer(Uint32 ms,SDL_TimerCallback callback)242 int SDL_SetTimer(Uint32 ms, SDL_TimerCallback callback)
243 {
244 int retval;
245
246 #ifdef DEBUG_TIMERS
247 printf("SDL_SetTimer(%d)\n", ms);
248 #endif
249 retval = 0;
250
251 if ( SDL_timer_threaded ) {
252 SDL_mutexP(SDL_timer_mutex);
253 }
254 if ( SDL_timer_running ) { /* Stop any currently running timer */
255 if ( SDL_timer_threaded ) {
256 while ( SDL_timers ) {
257 SDL_TimerID freeme = SDL_timers;
258 SDL_timers = SDL_timers->next;
259 SDL_free(freeme);
260 }
261 SDL_timer_running = 0;
262 list_changed = SDL_TRUE;
263 } else {
264 SDL_SYS_StopTimer();
265 SDL_timer_running = 0;
266 }
267 }
268 if ( ms ) {
269 if ( SDL_timer_threaded ) {
270 if ( SDL_AddTimerInternal(ms, callback_wrapper, (void *)callback) == NULL ) {
271 retval = -1;
272 }
273 } else {
274 SDL_timer_running = 1;
275 SDL_alarm_interval = ms;
276 SDL_alarm_callback = callback;
277 retval = SDL_SYS_StartTimer();
278 }
279 }
280 if ( SDL_timer_threaded ) {
281 SDL_mutexV(SDL_timer_mutex);
282 }
283
284 return retval;
285 }
286