src/timer/SDL_timer.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. SDL_SetTimerThreaded
  2. SDL_TimerInit
  3. SDL_TimerQuit
  4. SDL_ThreadedTimerCheck
  5. SDL_AddTimer
  6. SDL_RemoveTimer
  7. SDL_RemoveAllTimers
  8. callback_wrapper
  9. SDL_SetTimer

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

/* [<][>][^][v][top][bottom][index][help] */