Fix various problems with the timer code.
authorSam Lantinga <slouken@libsdl.org>
Thu, 13 Jan 2005 23:24:56 +0000
changeset 10285ba65305c954
parent 1027 c69697a85412
child 1029 f87f87efd45a
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
include/SDL_timer.h
src/events/SDL_events.c
src/timer/SDL_timer.c
     1.1 --- a/include/SDL_timer.h	Wed Jan 12 19:38:24 2005 +0000
     1.2 +++ b/include/SDL_timer.h	Thu Jan 13 23:24:56 2005 +0000
     1.3 @@ -81,6 +81,8 @@
     1.4   * in the same program, as it is implemented using setitimer().  You also
     1.5   * should not use this function in multi-threaded applications as signals
     1.6   * to multi-threaded apps have undefined behavior in some implementations.
     1.7 + *
     1.8 + * This function returns 0 if successful, or -1 if there was an error.
     1.9   */
    1.10  extern DECLSPEC int SDLCALL SDL_SetTimer(Uint32 interval, SDL_TimerCallback callback);
    1.11  
     2.1 --- a/src/events/SDL_events.c	Wed Jan 12 19:38:24 2005 +0000
     2.2 +++ b/src/events/SDL_events.c	Thu Jan 13 23:24:56 2005 +0000
     2.3 @@ -91,7 +91,6 @@
     2.4  
     2.5  static int SDL_GobbleEvents(void *unused)
     2.6  {
     2.7 -	SDL_SetTimerThreaded(2);
     2.8  	event_thread = SDL_ThreadID();
     2.9  	while ( SDL_EventQ.active ) {
    2.10  		SDL_VideoDevice *video = current_video;
    2.11 @@ -114,7 +113,7 @@
    2.12  
    2.13  		/* Give up the CPU for the rest of our timeslice */
    2.14  		SDL_EventLock.safe = 1;
    2.15 -		if( SDL_timer_running ) {
    2.16 +		if ( SDL_timer_running ) {
    2.17  			SDL_ThreadedTimerCheck();
    2.18  		}
    2.19  		SDL_Delay(1);
    2.20 @@ -162,6 +161,8 @@
    2.21  		}
    2.22  		SDL_EventLock.safe = 0;
    2.23  
    2.24 +		/* The event thread will handle timers too */
    2.25 +		SDL_SetTimerThreaded(2);
    2.26  		SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
    2.27  		if ( SDL_EventThread == NULL ) {
    2.28  			return(-1);
     3.1 --- a/src/timer/SDL_timer.c	Wed Jan 12 19:38:24 2005 +0000
     3.2 +++ b/src/timer/SDL_timer.c	Thu Jan 13 23:24:56 2005 +0000
     3.3 @@ -45,8 +45,6 @@
     3.4  Uint32 SDL_alarm_interval = 0;
     3.5  SDL_TimerCallback SDL_alarm_callback;
     3.6  
     3.7 -static volatile SDL_bool list_changed = SDL_FALSE;
     3.8 -
     3.9  /* Data used for a thread-based timer */
    3.10  static int SDL_timer_threaded = 0;
    3.11  
    3.12 @@ -59,8 +57,8 @@
    3.13  };
    3.14  
    3.15  static SDL_TimerID SDL_timers = NULL;
    3.16 -static Uint32 num_timers = 0;
    3.17  static SDL_mutex *SDL_timer_mutex;
    3.18 +static volatile SDL_bool list_changed = SDL_FALSE;
    3.19  
    3.20  /* Set whether or not the timer should use a thread.
    3.21     This should not be called while the timer subsystem is running.
    3.22 @@ -83,16 +81,19 @@
    3.23  {
    3.24  	int retval;
    3.25  
    3.26 -	SDL_timer_running = 0;
    3.27 -	SDL_SetTimer(0, NULL);
    3.28  	retval = 0;
    3.29 +	if ( SDL_timer_started ) {
    3.30 +		SDL_TimerQuit();
    3.31 +	}
    3.32  	if ( ! SDL_timer_threaded ) {
    3.33  		retval = SDL_SYS_TimerInit();
    3.34  	}
    3.35  	if ( SDL_timer_threaded ) {
    3.36  		SDL_timer_mutex = SDL_CreateMutex();
    3.37  	}
    3.38 -	SDL_timer_started = 1;
    3.39 +	if ( retval == 0 ) {
    3.40 +		SDL_timer_started = 1;
    3.41 +	}
    3.42  	return(retval);
    3.43  }
    3.44  
    3.45 @@ -113,43 +114,41 @@
    3.46  {
    3.47  	Uint32 now, ms;
    3.48  	SDL_TimerID t, prev, next;
    3.49 -	int removed;
    3.50 -	SDL_NewTimerCallback callback;
    3.51 -	Uint32 interval;
    3.52 -	void *param;
    3.53 -
    3.54 -	now = SDL_GetTicks();
    3.55 +	SDL_bool removed;
    3.56  
    3.57  	SDL_mutexP(SDL_timer_mutex);
    3.58 +	list_changed = SDL_FALSE;
    3.59 +	now = SDL_GetTicks();
    3.60  	for ( prev = NULL, t = SDL_timers; t; t = next ) {
    3.61 -		removed = 0;
    3.62 +		removed = SDL_FALSE;
    3.63  		ms = t->interval - SDL_TIMESLICE;
    3.64  		next = t->next;
    3.65 -		if ( (t->last_alarm < now) && ((now - t->last_alarm) > ms) ) {
    3.66 +		if ( (int)(now - t->last_alarm) > (int)ms ) {
    3.67 +			struct _SDL_TimerID timer;
    3.68 +
    3.69  			if ( (now - t->last_alarm) < t->interval ) {
    3.70  				t->last_alarm += t->interval;
    3.71  			} else {
    3.72  				t->last_alarm = now;
    3.73  			}
    3.74 -			list_changed = SDL_FALSE;
    3.75  #ifdef DEBUG_TIMERS
    3.76  			printf("Executing timer %p (thread = %d)\n",
    3.77 -						t, SDL_ThreadID());
    3.78 +				t, SDL_ThreadID());
    3.79  #endif
    3.80 -			callback = t->cb;
    3.81 -			interval = t->interval;
    3.82 -			param = t->param;
    3.83 +			timer = *t;
    3.84  			SDL_mutexV(SDL_timer_mutex);
    3.85 -			ms = callback(interval, param);
    3.86 +			ms = timer.cb(timer.interval, timer.param);
    3.87  			SDL_mutexP(SDL_timer_mutex);
    3.88  			if ( list_changed ) {
    3.89 -				/* Abort, list of timers has been modified */
    3.90 +				/* Abort, list of timers modified */
    3.91 +				/* FIXME: what if ms was changed? */
    3.92  				break;
    3.93  			}
    3.94  			if ( ms != t->interval ) {
    3.95  				if ( ms ) {
    3.96  					t->interval = ROUND_RESOLUTION(ms);
    3.97 -				} else { /* Remove the timer from the linked list */
    3.98 +				} else {
    3.99 +					/* Remove timer from the list */
   3.100  #ifdef DEBUG_TIMERS
   3.101  					printf("SDL: Removing timer %p\n", t);
   3.102  #endif
   3.103 @@ -159,8 +158,8 @@
   3.104  						SDL_timers = next;
   3.105  					}
   3.106  					free(t);
   3.107 -					-- num_timers;
   3.108 -					removed = 1;
   3.109 +					--SDL_timer_running;
   3.110 +					removed = SDL_TRUE;
   3.111  				}
   3.112  			}
   3.113  		}
   3.114 @@ -172,6 +171,26 @@
   3.115  	SDL_mutexV(SDL_timer_mutex);
   3.116  }
   3.117  
   3.118 +static SDL_TimerID SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback, void *param)
   3.119 +{
   3.120 +	SDL_TimerID t;
   3.121 +	t = (SDL_TimerID) malloc(sizeof(struct _SDL_TimerID));
   3.122 +	if ( t ) {
   3.123 +		t->interval = ROUND_RESOLUTION(interval);
   3.124 +		t->cb = callback;
   3.125 +		t->param = param;
   3.126 +		t->last_alarm = SDL_GetTicks();
   3.127 +		t->next = SDL_timers;
   3.128 +		SDL_timers = t;
   3.129 +		++SDL_timer_running;
   3.130 +		list_changed = SDL_TRUE;
   3.131 +	}
   3.132 +#ifdef DEBUG_TIMERS
   3.133 +	printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32)t, SDL_timer_running);
   3.134 +#endif
   3.135 +	return t;
   3.136 +}
   3.137 +
   3.138  SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)
   3.139  {
   3.140  	SDL_TimerID t;
   3.141 @@ -188,21 +207,7 @@
   3.142  		return NULL;
   3.143  	}
   3.144  	SDL_mutexP(SDL_timer_mutex);
   3.145 -	t = (SDL_TimerID) malloc(sizeof(struct _SDL_TimerID));
   3.146 -	if ( t ) {
   3.147 -		t->interval = ROUND_RESOLUTION(interval);
   3.148 -		t->cb = callback;
   3.149 -		t->param = param;
   3.150 -		t->last_alarm = SDL_GetTicks();
   3.151 -		t->next = SDL_timers;
   3.152 -		SDL_timers = t;
   3.153 -		++ num_timers;
   3.154 -		list_changed = SDL_TRUE;
   3.155 -		SDL_timer_running = 1;
   3.156 -	}
   3.157 -#ifdef DEBUG_TIMERS
   3.158 -	printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32)t, num_timers);
   3.159 -#endif
   3.160 +	t = SDL_AddTimerInternal(interval, callback, param);
   3.161  	SDL_mutexV(SDL_timer_mutex);
   3.162  	return t;
   3.163  }
   3.164 @@ -223,34 +228,19 @@
   3.165  				SDL_timers = t->next;
   3.166  			}
   3.167  			free(t);
   3.168 -			-- num_timers;
   3.169 +			--SDL_timer_running;
   3.170  			removed = SDL_TRUE;
   3.171  			list_changed = SDL_TRUE;
   3.172  			break;
   3.173  		}
   3.174  	}
   3.175  #ifdef DEBUG_TIMERS
   3.176 -	printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, num_timers, SDL_ThreadID());
   3.177 +	printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, SDL_timer_running, SDL_ThreadID());
   3.178  #endif
   3.179  	SDL_mutexV(SDL_timer_mutex);
   3.180  	return removed;
   3.181  }
   3.182  
   3.183 -static void SDL_RemoveAllTimers(SDL_TimerID t)
   3.184 -{
   3.185 -	SDL_TimerID freeme;
   3.186 -
   3.187 -	/* Changed to non-recursive implementation.
   3.188 -	   The recursive implementation is elegant, but subject to 
   3.189 -	   stack overflow if there are lots and lots of timers.
   3.190 -	 */
   3.191 -	while ( t ) {
   3.192 -		freeme = t;
   3.193 -		t = t->next;
   3.194 -		free(freeme);
   3.195 -	}
   3.196 -}
   3.197 -
   3.198  /* Old style callback functions are wrapped through this */
   3.199  static Uint32 callback_wrapper(Uint32 ms, void *param)
   3.200  {
   3.201 @@ -266,21 +256,29 @@
   3.202  	printf("SDL_SetTimer(%d)\n", ms);
   3.203  #endif
   3.204  	retval = 0;
   3.205 +
   3.206 +	if ( SDL_timer_threaded ) {
   3.207 +		SDL_mutexP(SDL_timer_mutex);
   3.208 +	}
   3.209  	if ( SDL_timer_running ) {	/* Stop any currently running timer */
   3.210 -		SDL_timer_running = 0;
   3.211  		if ( SDL_timer_threaded ) {
   3.212 -			SDL_mutexP(SDL_timer_mutex);
   3.213 -			SDL_RemoveAllTimers(SDL_timers);
   3.214 -			SDL_timers = NULL;
   3.215 -			SDL_mutexV(SDL_timer_mutex);
   3.216 +			while ( SDL_timers ) {
   3.217 +				SDL_TimerID freeme = SDL_timers;
   3.218 +				SDL_timers = SDL_timers->next;
   3.219 +				free(freeme);
   3.220 +			}
   3.221 +			SDL_timer_running = 0;
   3.222 +			list_changed = SDL_TRUE;
   3.223  		} else {
   3.224  			SDL_SYS_StopTimer();
   3.225 +			SDL_timer_running = 0;
   3.226  		}
   3.227  	}
   3.228  	if ( ms ) {
   3.229  		if ( SDL_timer_threaded ) {
   3.230 -			retval = (SDL_AddTimer(ms, callback_wrapper,
   3.231 -					       (void *)callback) != NULL);
   3.232 +			if ( SDL_AddTimerInternal(ms, callback_wrapper, (void *)callback) == NULL ) {
   3.233 +				retval = -1;
   3.234 +			}
   3.235  		} else {
   3.236  			SDL_timer_running = 1;
   3.237  			SDL_alarm_interval = ms;
   3.238 @@ -288,5 +286,9 @@
   3.239  			retval = SDL_SYS_StartTimer();
   3.240  		}
   3.241  	}
   3.242 +	if ( SDL_timer_threaded ) {
   3.243 +		SDL_mutexP(SDL_timer_mutex);
   3.244 +	}
   3.245 +
   3.246  	return retval;
   3.247  }