Mercurial > sdl-ios-xcode
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 } |