comparison src/timer/SDL_timer.c @ 1028:5ba65305c954

Fix various problems with the timer code. * SDL_timer_running wasn't always updated correctly. * Fixed occasional crash in SDL_SetTimer() when clearing threaded timers * It was possible to get both the timer thread and event thread running * Other misc. cleanup
author Sam Lantinga <slouken@libsdl.org>
date Thu, 13 Jan 2005 23:24:56 +0000
parents e719ee25439d
children 95b261f445b1
comparison
equal deleted inserted replaced
1027:c69697a85412 1028:5ba65305c954
42 int SDL_timer_running = 0; 42 int SDL_timer_running = 0;
43 43
44 /* Data to handle a single periodic alarm */ 44 /* Data to handle a single periodic alarm */
45 Uint32 SDL_alarm_interval = 0; 45 Uint32 SDL_alarm_interval = 0;
46 SDL_TimerCallback SDL_alarm_callback; 46 SDL_TimerCallback SDL_alarm_callback;
47
48 static volatile SDL_bool list_changed = SDL_FALSE;
49 47
50 /* Data used for a thread-based timer */ 48 /* Data used for a thread-based timer */
51 static int SDL_timer_threaded = 0; 49 static int SDL_timer_threaded = 0;
52 50
53 struct _SDL_TimerID { 51 struct _SDL_TimerID {
57 Uint32 last_alarm; 55 Uint32 last_alarm;
58 struct _SDL_TimerID *next; 56 struct _SDL_TimerID *next;
59 }; 57 };
60 58
61 static SDL_TimerID SDL_timers = NULL; 59 static SDL_TimerID SDL_timers = NULL;
62 static Uint32 num_timers = 0;
63 static SDL_mutex *SDL_timer_mutex; 60 static SDL_mutex *SDL_timer_mutex;
61 static volatile SDL_bool list_changed = SDL_FALSE;
64 62
65 /* Set whether or not the timer should use a thread. 63 /* Set whether or not the timer should use a thread.
66 This should not be called while the timer subsystem is running. 64 This should not be called while the timer subsystem is running.
67 */ 65 */
68 int SDL_SetTimerThreaded(int value) 66 int SDL_SetTimerThreaded(int value)
81 79
82 int SDL_TimerInit(void) 80 int SDL_TimerInit(void)
83 { 81 {
84 int retval; 82 int retval;
85 83
86 SDL_timer_running = 0;
87 SDL_SetTimer(0, NULL);
88 retval = 0; 84 retval = 0;
85 if ( SDL_timer_started ) {
86 SDL_TimerQuit();
87 }
89 if ( ! SDL_timer_threaded ) { 88 if ( ! SDL_timer_threaded ) {
90 retval = SDL_SYS_TimerInit(); 89 retval = SDL_SYS_TimerInit();
91 } 90 }
92 if ( SDL_timer_threaded ) { 91 if ( SDL_timer_threaded ) {
93 SDL_timer_mutex = SDL_CreateMutex(); 92 SDL_timer_mutex = SDL_CreateMutex();
94 } 93 }
95 SDL_timer_started = 1; 94 if ( retval == 0 ) {
95 SDL_timer_started = 1;
96 }
96 return(retval); 97 return(retval);
97 } 98 }
98 99
99 void SDL_TimerQuit(void) 100 void SDL_TimerQuit(void)
100 { 101 {
111 112
112 void SDL_ThreadedTimerCheck(void) 113 void SDL_ThreadedTimerCheck(void)
113 { 114 {
114 Uint32 now, ms; 115 Uint32 now, ms;
115 SDL_TimerID t, prev, next; 116 SDL_TimerID t, prev, next;
116 int removed; 117 SDL_bool removed;
117 SDL_NewTimerCallback callback; 118
118 Uint32 interval; 119 SDL_mutexP(SDL_timer_mutex);
119 void *param; 120 list_changed = SDL_FALSE;
120
121 now = SDL_GetTicks(); 121 now = SDL_GetTicks();
122
123 SDL_mutexP(SDL_timer_mutex);
124 for ( prev = NULL, t = SDL_timers; t; t = next ) { 122 for ( prev = NULL, t = SDL_timers; t; t = next ) {
125 removed = 0; 123 removed = SDL_FALSE;
126 ms = t->interval - SDL_TIMESLICE; 124 ms = t->interval - SDL_TIMESLICE;
127 next = t->next; 125 next = t->next;
128 if ( (t->last_alarm < now) && ((now - t->last_alarm) > ms) ) { 126 if ( (int)(now - t->last_alarm) > (int)ms ) {
127 struct _SDL_TimerID timer;
128
129 if ( (now - t->last_alarm) < t->interval ) { 129 if ( (now - t->last_alarm) < t->interval ) {
130 t->last_alarm += t->interval; 130 t->last_alarm += t->interval;
131 } else { 131 } else {
132 t->last_alarm = now; 132 t->last_alarm = now;
133 } 133 }
134 list_changed = SDL_FALSE;
135 #ifdef DEBUG_TIMERS 134 #ifdef DEBUG_TIMERS
136 printf("Executing timer %p (thread = %d)\n", 135 printf("Executing timer %p (thread = %d)\n",
137 t, SDL_ThreadID()); 136 t, SDL_ThreadID());
138 #endif 137 #endif
139 callback = t->cb; 138 timer = *t;
140 interval = t->interval;
141 param = t->param;
142 SDL_mutexV(SDL_timer_mutex); 139 SDL_mutexV(SDL_timer_mutex);
143 ms = callback(interval, param); 140 ms = timer.cb(timer.interval, timer.param);
144 SDL_mutexP(SDL_timer_mutex); 141 SDL_mutexP(SDL_timer_mutex);
145 if ( list_changed ) { 142 if ( list_changed ) {
146 /* Abort, list of timers has been modified */ 143 /* Abort, list of timers modified */
144 /* FIXME: what if ms was changed? */
147 break; 145 break;
148 } 146 }
149 if ( ms != t->interval ) { 147 if ( ms != t->interval ) {
150 if ( ms ) { 148 if ( ms ) {
151 t->interval = ROUND_RESOLUTION(ms); 149 t->interval = ROUND_RESOLUTION(ms);
152 } else { /* Remove the timer from the linked list */ 150 } else {
151 /* Remove timer from the list */
153 #ifdef DEBUG_TIMERS 152 #ifdef DEBUG_TIMERS
154 printf("SDL: Removing timer %p\n", t); 153 printf("SDL: Removing timer %p\n", t);
155 #endif 154 #endif
156 if ( prev ) { 155 if ( prev ) {
157 prev->next = next; 156 prev->next = next;
158 } else { 157 } else {
159 SDL_timers = next; 158 SDL_timers = next;
160 } 159 }
161 free(t); 160 free(t);
162 -- num_timers; 161 --SDL_timer_running;
163 removed = 1; 162 removed = SDL_TRUE;
164 } 163 }
165 } 164 }
166 } 165 }
167 /* Don't update prev if the timer has disappeared */ 166 /* Don't update prev if the timer has disappeared */
168 if ( ! removed ) { 167 if ( ! removed ) {
170 } 169 }
171 } 170 }
172 SDL_mutexV(SDL_timer_mutex); 171 SDL_mutexV(SDL_timer_mutex);
173 } 172 }
174 173
175 SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param) 174 static SDL_TimerID SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback, void *param)
176 { 175 {
177 SDL_TimerID t; 176 SDL_TimerID t;
178 if ( ! SDL_timer_mutex ) {
179 if ( SDL_timer_started ) {
180 SDL_SetError("This platform doesn't support multiple timers");
181 } else {
182 SDL_SetError("You must call SDL_Init(SDL_INIT_TIMER) first");
183 }
184 return NULL;
185 }
186 if ( ! SDL_timer_threaded ) {
187 SDL_SetError("Multiple timers require threaded events!");
188 return NULL;
189 }
190 SDL_mutexP(SDL_timer_mutex);
191 t = (SDL_TimerID) malloc(sizeof(struct _SDL_TimerID)); 177 t = (SDL_TimerID) malloc(sizeof(struct _SDL_TimerID));
192 if ( t ) { 178 if ( t ) {
193 t->interval = ROUND_RESOLUTION(interval); 179 t->interval = ROUND_RESOLUTION(interval);
194 t->cb = callback; 180 t->cb = callback;
195 t->param = param; 181 t->param = param;
196 t->last_alarm = SDL_GetTicks(); 182 t->last_alarm = SDL_GetTicks();
197 t->next = SDL_timers; 183 t->next = SDL_timers;
198 SDL_timers = t; 184 SDL_timers = t;
199 ++ num_timers; 185 ++SDL_timer_running;
200 list_changed = SDL_TRUE; 186 list_changed = SDL_TRUE;
201 SDL_timer_running = 1; 187 }
202 } 188 #ifdef DEBUG_TIMERS
203 #ifdef DEBUG_TIMERS 189 printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32)t, SDL_timer_running);
204 printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32)t, num_timers); 190 #endif
205 #endif 191 return t;
192 }
193
194 SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)
195 {
196 SDL_TimerID t;
197 if ( ! SDL_timer_mutex ) {
198 if ( SDL_timer_started ) {
199 SDL_SetError("This platform doesn't support multiple timers");
200 } else {
201 SDL_SetError("You must call SDL_Init(SDL_INIT_TIMER) first");
202 }
203 return NULL;
204 }
205 if ( ! SDL_timer_threaded ) {
206 SDL_SetError("Multiple timers require threaded events!");
207 return NULL;
208 }
209 SDL_mutexP(SDL_timer_mutex);
210 t = SDL_AddTimerInternal(interval, callback, param);
206 SDL_mutexV(SDL_timer_mutex); 211 SDL_mutexV(SDL_timer_mutex);
207 return t; 212 return t;
208 } 213 }
209 214
210 SDL_bool SDL_RemoveTimer(SDL_TimerID id) 215 SDL_bool SDL_RemoveTimer(SDL_TimerID id)
221 prev->next = t->next; 226 prev->next = t->next;
222 } else { 227 } else {
223 SDL_timers = t->next; 228 SDL_timers = t->next;
224 } 229 }
225 free(t); 230 free(t);
226 -- num_timers; 231 --SDL_timer_running;
227 removed = SDL_TRUE; 232 removed = SDL_TRUE;
228 list_changed = SDL_TRUE; 233 list_changed = SDL_TRUE;
229 break; 234 break;
230 } 235 }
231 } 236 }
232 #ifdef DEBUG_TIMERS 237 #ifdef DEBUG_TIMERS
233 printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, num_timers, SDL_ThreadID()); 238 printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, SDL_timer_running, SDL_ThreadID());
234 #endif 239 #endif
235 SDL_mutexV(SDL_timer_mutex); 240 SDL_mutexV(SDL_timer_mutex);
236 return removed; 241 return removed;
237 } 242 }
238 243
239 static void SDL_RemoveAllTimers(SDL_TimerID t)
240 {
241 SDL_TimerID freeme;
242
243 /* Changed to non-recursive implementation.
244 The recursive implementation is elegant, but subject to
245 stack overflow if there are lots and lots of timers.
246 */
247 while ( t ) {
248 freeme = t;
249 t = t->next;
250 free(freeme);
251 }
252 }
253
254 /* Old style callback functions are wrapped through this */ 244 /* Old style callback functions are wrapped through this */
255 static Uint32 callback_wrapper(Uint32 ms, void *param) 245 static Uint32 callback_wrapper(Uint32 ms, void *param)
256 { 246 {
257 SDL_TimerCallback func = (SDL_TimerCallback) param; 247 SDL_TimerCallback func = (SDL_TimerCallback) param;
258 return (*func)(ms); 248 return (*func)(ms);
264 254
265 #ifdef DEBUG_TIMERS 255 #ifdef DEBUG_TIMERS
266 printf("SDL_SetTimer(%d)\n", ms); 256 printf("SDL_SetTimer(%d)\n", ms);
267 #endif 257 #endif
268 retval = 0; 258 retval = 0;
259
260 if ( SDL_timer_threaded ) {
261 SDL_mutexP(SDL_timer_mutex);
262 }
269 if ( SDL_timer_running ) { /* Stop any currently running timer */ 263 if ( SDL_timer_running ) { /* Stop any currently running timer */
270 SDL_timer_running = 0;
271 if ( SDL_timer_threaded ) { 264 if ( SDL_timer_threaded ) {
272 SDL_mutexP(SDL_timer_mutex); 265 while ( SDL_timers ) {
273 SDL_RemoveAllTimers(SDL_timers); 266 SDL_TimerID freeme = SDL_timers;
274 SDL_timers = NULL; 267 SDL_timers = SDL_timers->next;
275 SDL_mutexV(SDL_timer_mutex); 268 free(freeme);
269 }
270 SDL_timer_running = 0;
271 list_changed = SDL_TRUE;
276 } else { 272 } else {
277 SDL_SYS_StopTimer(); 273 SDL_SYS_StopTimer();
274 SDL_timer_running = 0;
278 } 275 }
279 } 276 }
280 if ( ms ) { 277 if ( ms ) {
281 if ( SDL_timer_threaded ) { 278 if ( SDL_timer_threaded ) {
282 retval = (SDL_AddTimer(ms, callback_wrapper, 279 if ( SDL_AddTimerInternal(ms, callback_wrapper, (void *)callback) == NULL ) {
283 (void *)callback) != NULL); 280 retval = -1;
281 }
284 } else { 282 } else {
285 SDL_timer_running = 1; 283 SDL_timer_running = 1;
286 SDL_alarm_interval = ms; 284 SDL_alarm_interval = ms;
287 SDL_alarm_callback = callback; 285 SDL_alarm_callback = callback;
288 retval = SDL_SYS_StartTimer(); 286 retval = SDL_SYS_StartTimer();
289 } 287 }
290 } 288 }
289 if ( SDL_timer_threaded ) {
290 SDL_mutexP(SDL_timer_mutex);
291 }
292
291 return retval; 293 return retval;
292 } 294 }