Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Two timers, one function

Credits go to CGamesPlay and ReyBrujo for helping out!
This thread is locked; no one can reply to it. rss feed Print
Two timers, one function
Biznaga
Member #3,180
January 2003
avatar

Hi!
Is it safe install two timers that point to the same function?
I want to do something like this:

1void timer_func(void* p)
2{
3 int i = *((int*) p);
4 switch (i) { ... }
5}
6END_OF_FUNCTION(timer_func);
7 
8void install_timers()
9{
10 volatile int param1 = 1;
11 volatile int param2 = 2;
12 
13 LOCK_VARIABLE(param1);
14 LOCK_VARIABLE(param2);
15 LOCK_FUNCTION(timer_func);
16 install_param_int_ex(timer_func, (void*) &param1, BPS_TO_TIMER(60));
17 install_param_int_ex(timer_func, (void*) &param2, BPS_TO_TIMER(60));
18}

CGamesPlay
Member #2,559
July 2002
avatar

That's perfectly valid. Try it out :P

[append]
Oh, well, it's valid, but the address of those two params goes out of scope when the functions return. It will cause lots of crashes later :-* To fix, move the params to a scope that won't be overwritten for the lifetime of the timers (i.e. globals).

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

ReyBrujo
Moderator
January 2001
avatar

Remember that your function must be fast. If it is fast, then you should have no problems.

--
RB
光子「あたしただ…奪う側に回ろうと思っただけよ」
Mitsuko's last words, Battle Royale

Biznaga
Member #3,180
January 2003
avatar

I'll assume it's OK then. Thanks, guys!

CGamesPlay said:

Oh, well, it's valid, but the address of those two params goes out of scope when the functions return.

A minor detail :P
Of course those vars will be global or declared in the main game loop. I missed that in the example above, sorry. ;D

ReyBrujo said:

Remember that your function must be fast.

That's the idea, it'll just update some flags.

volatile int[MAX] params;
...
void timer_func(void* p)
{
  int index = *((int*) p);
  ++params[index];
}

The point is I have this doubt mainly because I know nothing about interrupts and threads. I don't know if something weird happens with the stack or whatever when accessing the same function at the same time. My game is still in the design stage, so I don't want any weird bug later.

Dustin Dettmer
Member #3,935
October 2003
avatar

Quote:

The point is I have this doubt mainly because I know nothing about interrupts and threads. I don't know if something weird happens with the stack or whatever when accessing the same function at the same time. My game is still in the design stage, so I don't want any weird bug later.

In most cases they should be called from completely different stacks.

Quote:

volatile int[MAX] params;
...
void timer_func(void* p)
{
int index = ((int) p);
++params[index];
}

The typical way to solve this problem is pass timer_func a pointer to a struct with all the data you need in it, instead of making global varaibles.

Example:

1struct TimerInfo {
2 int i;
3 char c;
4};
5 
6void timer_func(void* p)
7{
8 TimerInfo &t = *(TimerInfo*)p;
9 
10 switch(t.i) {
11 case 0: cout << t.c << endl;
12 }
13}
14END_OF_FUNCTION(timer_func);
15 
16void install_timers()
17{
18 static TimerInfo t1 = { 0, 'A' };
19 static TimerInfo t2 = { 2, 'B' };
20 
21 LOCK_FUNCTION(timer_func);
22 install_param_int_ex(timer_func, (void*) &t1, BPS_TO_TIMER(60));
23 install_param_int_ex(timer_func, (void*) &t2, BPS_TO_TIMER(60));
24}

Tobias Dammers
Member #2,604
August 2002
avatar

And again I say: Don't - use - allegro - timers! They're inaccurate, they eat (too) much cpu, and they require extra threads, without removing the need for constant polling (which is the only advantage of using threads after all).
QueryPerformanceCounter() (windows), gettimeofday (linux) are cheaper (in terms of system resources - basically, they run anyway, so all you need to do is read them once per main loop pass), thread-safe, way more accurate (down to a single cpu clock tick) and easier to code (once you wrap them right; do it once and re-use for all future projects). Only fall back on allegro timers if you absolutely have to.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

ReyBrujo
Moderator
January 2001
avatar

Unless you are making a 3D (especially a FPS), Allegro timers are fine and portable enough.

--
RB
光子「あたしただ…奪う側に回ろうと思っただけよ」
Mitsuko's last words, Battle Royale

Sirocco
Member #88
April 2000
avatar

I completely agree with Rey. I use Allegro timers (and lots of them) for all of my games, and they work perfectly. They exist for a reason, despite their few shortcomings, and if you understand how they work and how they are meant to be deployed you won't have any problems. If the situation demands a different approach (such as complex 3D systems), take it.

Edited for clarity.

-->
Graphic file formats used to fascinate me, but now I find them rather satanic.

BAF
Member #2,981
December 2002
avatar

If anybody had some framework for basic QPC/gettimeofday that can be reused easily, that would be nice. :-*

GullRaDriel
Member #3,861
September 2003
avatar

Baf said:

If anybody had some framework for basic QPC/gettimeofday that can be reused easily, that would be nice.

Search for timer in the forum and write your own.

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

timers [...] require extra threads

All of Allegro's timers run on a single thread. And since some of Allegro's internals use timers, that thread is already created.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Tobias Dammers
Member #2,604
August 2002
avatar

This is what I use:
timer.h:

#ifndef TIMER_H
#define TIMER_H

void init_timer(bool use_qpc = false);
void stop_timer();
void start_timer(int timer_ms);
float get_timer_delta();
const char* get_timing_method_string();
float get_timer_accuracy();

#endif

timer.cpp:

1#include <allegro.h>
2#ifdef ALLEGRO_WINDOWS
3 #include <winalleg.h>
4#endif
5#include "timer.h"
6 
7bool initialized = false;
8 
9int timer = 0;
10int timer_ms = 0;
11 
12#ifdef ALLEGRO_WINDOWS
13LARGE_INTEGER last_perf_count_li;
14LARGE_INTEGER perf_count_li;
15LARGE_INTEGER perf_freq_li;
16unsigned long long last_perf_count;
17unsigned long long perf_count;
18unsigned long long perf_freq;
19 
20unsigned long long li_to_ll(LARGE_INTEGER li) {
21 unsigned long long ll = li.HighPart;
22 ll = ll << 32;
23 ll |= li.LowPart;
24 return ll;
25}
26#endif
27 
28bool qpc_mode = false;
29 
30void timer_proc() {
31 ++timer;
32}
33END_OF_FUNCTION(timer_proc);
34 
35void init_timer(bool use_qpc) {
36#ifdef ALLEGRO_WINDOWS
37 if (use_qpc)
38 qpc_mode = QueryPerformanceFrequency(&perf_freq_li);
39 else
40#endif
41 qpc_mode = false;
42
43 if (qpc_mode)
44 perf_freq = li_to_ll(perf_freq_li);
45
46 if (!qpc_mode) {
47 LOCK_VARIABLE(timer);
48 LOCK_FUNCTION(timer_proc);
49 install_timer();
50 }
51
52 initialized = true;
53}
54 
55void stop_timer() {
56 if (!qpc_mode)
57 remove_int(timer_proc);
58}
59 
60void start_timer(int _timer_ms) {
61 if (qpc_mode) {
62#ifdef ALLEGRO_WINDOWS
63 QueryPerformanceCounter(&last_perf_count_li);
64 last_perf_count = li_to_ll(last_perf_count_li);
65#endif
66 }
67 else {
68 stop_timer();
69 timer_ms = _timer_ms;
70 install_int(timer_proc, timer_ms);
71 }
72}
73 
74float get_timer_delta() {
75 if (qpc_mode) {
76#ifdef ALLEGRO_WINDOWS
77 QueryPerformanceCounter(&perf_count_li);
78 perf_count = li_to_ll(perf_count_li);
79 float result = (float)((double)(perf_count - last_perf_count) / (double)perf_freq);
80 last_perf_count = perf_count;
81 return result;
82#else
83 return NULL;
84#endif
85 }
86 else {
87 float result = (float)timer * (float)timer_ms * 0.001f;
88 timer = 0;
89 return result;
90 }
91}
92 
93const char* get_timing_method_string() {
94 if (!initialized)
95 return "Not initialized!";
96 if (qpc_mode)
97#ifdef ALLEGRO_WINDOWS
98 return "QueryPerformanceCounter";
99#else
100 return "Something's terribly wrong";
101#endif
102 return "Allegro timer routines";
103}
104 
105float get_timer_accuracy() {
106 if (qpc_mode)
107#ifdef ALLEGRO_WINDOWS
108 return (float)perf_freq;
109#else
110 return 0.0f;
111#endif
112 if (timer_ms)
113 return 1000.0f / (float)timer_ms;
114 return 0.0f;
115}

Uses QPC in windows if asked to, and falls back on allegro timers otherwise.
Doesn't support gettimeofday() yet, since I don't have a linux box running, but it can easily be extended. You can also hack it at will to use integers to represent time, or libc-time, or whatever you please. You may also want to put the whole thing into a class, or make it C-friendly.
Oh yes, and feel free to re-use.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

Dustin Dettmer
Member #3,935
October 2003
avatar

Baf said:

If anybody had some framework for basic QPC/gettimeofday that can be reused easily, that would be nice. :-*

Put it in my domain's temp folder. If you want to mirror it baf it would last longer:
http://wipproject.zapto.org/tmp/timer.h
http://wipproject.zapto.org/tmp/timer.cpp

Usage:

#include "timer.h"

int main() {
  printf("%u seconds or %u milliseconds or %u microseconds since program start",
    timer.secs(),
    timer.msecs(),
    timer.usecs());
}

Go to: