• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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