1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2006 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 (at) 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 */ 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 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 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 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 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 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 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 */ 236 static Uint32 SDLCALL callback_wrapper(Uint32 ms, void *param) 237 { 238 SDL_TimerCallback func = (SDL_TimerCallback) param; 239 return (*func)(ms); 240 } 241 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