[AD] Possible Timer API additions/modifications

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Since allegro is a high-level library I thought it might be useful to add some common functionality to the timer API's. In particular it would be useful to have a stopwatch timer and a countdown timer. A stopwatch simply measures time elapsed. The interface provides for pausing, resuming, and resetting. The countdown timer will expire when the time remaining reaches 0. It has a nearly identical interface as the stopwatch. The stopwatch and countdown timers were done using doubles as they were the natural choice. These timers are implemented entirely on top of al_current_time().

I have attached a quick stand-alone prototype (for *nix) which includes a quick test bench for a feel of the interface.  I quickly re-implemented al_rest and al_current_time to deal with doubles as well for convenience sake.

--ryan
/**
Ryan Dickie
Jan 25, 2008

  TODO:
    1) how to error handle?
    2) multi-cpu gettimeofday issues? (microseconds off between cores)
       should not cause problems for 1ms or higher
    3) the gettimeofday is microsecond accurate but context switches/scheduling
       give it about 1.5ms accuracy.
       In fact, the tickless kernel uses the 1us accuracy of gettimeofday and
       nanosleep
    4) Cross-platform ;)	

    compile with -lm for floor(double)
*/

#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>

static struct timeval _init_time;

void _al_unix_init_time()
{
   gettimeofday(&_init_time, 0);
}

/*
	returns time since allegro started
*/
double al_current_time()
{
	struct timeval tp;
	gettimeofday(&tp, 0); //null = timezone afaik
	double time = (double) (tp.tv_sec - _init_time.tv_sec)
		+ (double) (tp.tv_usec - _init_time.tv_usec) * 1.0e-6;
	return time;
}

/**
	i found select would wake up slightly early
	nanosleep wakes up slightly late
	both ~ 1.5ms accurate
*/
void al_rest(double time)
{
	struct timespec tp;
	double seconds = floor(time);
	tp.tv_sec = (time_t) seconds;
	tp.tv_nsec = (suseconds_t) ((time - seconds) * 1e9);
	nanosleep(&tp,0);
}

/**
####################################################
########### One-Shot TIMER #########################
####################################################
should be parallel to allegro's periodic timer
Current Timers have a nice interface I think
just make it handle doubles.
*/



/**
####################################################
########### COUNTDOWN TIMER ************************
####################################################
*/

typedef struct al_countdown_timer
{
	/* timeofday timer started/resumed */
	double start_time;
	double time_left;
	char paused; //true or false
} al_countdown_timer;

/* sets/intializes timer. does not start */
void al_countdown_reset(al_countdown_timer* tim, double time)
{
	if (tim && time > 0)
	{
		tim->time_left = time;
		tim->paused = 1;
	}
}

/* starts or resumes */
void al_countdown_start(al_countdown_timer* tim)
{
	if (tim)
	{
		tim->start_time = al_current_time();
		tim->paused = 0;
	}
}

/* pauses or stops */
void al_countdown_stop(al_countdown_timer* tim)
{
	if (tim)
	{
		tim->time_left -= al_current_time() - tim->start_time;
		tim->paused = 1;
	}

}

/* gets time left. It returns 0 if the timer has elapsed */
double al_countdown_time(al_countdown_timer* tim)
{
	if (tim && !tim->paused)
	{
		tim->time_left -= al_current_time() - tim->start_time;
		tim->start_time = al_current_time();		
	}

	if (tim->time_left > 0)
		return tim->time_left;
	else
		return 0;
}

/**
####################################################
########### STOPWATCH TIMER ************************
####################################################
*/

typedef struct al_stopwatch_timer
{
	/* timeofday timer started/resumed */
	double start_time; 
	double elapsed;
	char paused;
} al_stopwatch_timer;

/* initializes or resets */
void al_stopwatch_reset(al_stopwatch_timer* tim)
{
	if (tim && time > 0)
	{
		tim->elapsed = 0;
		tim->paused = 1;
	}
}

/* starts timing */
void al_stopwatch_start(al_stopwatch_timer* tim)
{
	if (tim)
	{
		tim->start_time = al_current_time();
		tim->paused = 0;
	}
}

/* stops counter. equivalent to pause */
void al_stopwatch_stop(al_stopwatch_timer* tim)
{
	if (tim)
	{
		tim->elapsed += al_current_time() - tim->start_time;
		tim->paused = 1;
	}
}

/* gets current count of the timer */
double al_stopwatch_time(al_stopwatch_timer* tim)
{
	if (tim && !tim->paused)
	{
		tim->elapsed += al_current_time() - tim->start_time;
		tim->start_time = al_current_time();
	}
	return tim->elapsed;
}

/**
#####################################################
################ TEST BENCH #########################
#####################################################
*/

int main(int argc, char** argv)
{


	int i;
	_al_unix_init_time();

//	goto countdown_test;

delay_test: printf("Delay tests\n");
{
	printf("1000x 10ms delays\n");
	double start = al_current_time();
	for(i=0; i<1000; ++i)
		al_rest(.01);
	double end = al_current_time()-start;
	double error = end - (1000 * .01);
	printf("Time Slept %6f\n",end);
	printf("Cummulative error %6f\n", error);
	printf("Error per sleep %6f\n\n", error / 1000);
}
{
	printf("3x 3s delays\n");
	double start = al_current_time();
	for(i=0; i<3; ++i)
		al_rest(3);
	double end = al_current_time()-start;
	double error = end - 9;
	printf("Time Slept %6f\n",end);
	printf("Cummulative error %6f\n", error);
	printf("Error per sleep %6f\n\n", error / 9);
}
countdown_test: printf("Timer tests\n");
//countdown timer test
	al_countdown_timer tim1;
	al_stopwatch_timer tim2;
	al_countdown_reset(&tim1,50);
	al_stopwatch_reset(&tim2);
	printf("Countdown value %6f\n",al_countdown_time(&tim1));
	printf("Stopwatch value %6f\n",al_stopwatch_time(&tim2));
	al_countdown_start(&tim1);
	al_stopwatch_start(&tim2);
	printf("Countdown value %6f\n",al_countdown_time(&tim1));
	printf("Stopwatch value %6f\n",al_stopwatch_time(&tim2));
	printf("20 second sleep\n");
	al_rest(20.0);
	al_countdown_stop(&tim1);
	al_stopwatch_stop(&tim2);
	printf("countdown value %6f\n",al_countdown_time(&tim1));
	printf("Stopwatch value %6f\n",al_stopwatch_time(&tim2));
	printf("Pausing Timers and 2.795 second sleep\n");
	al_rest(2.795);
	printf("countdown value %6f\n",al_countdown_time(&tim1));
	printf("Stopwatch value %6f\n",al_stopwatch_time(&tim2));
	printf("Resuming Timers and 5 second sleep\n");
	al_countdown_start(&tim1);
	al_stopwatch_start(&tim2);
	al_rest(5.0);
	printf("Stopwatch value %6f\n",al_stopwatch_time(&tim2));
	printf("countdown value %6f\n",al_countdown_time(&tim1));	
	return 0;
}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/