Hi!
Is it safe install two timers that point to the same function?
I want to do something like this:
| 1 | void timer_func(void* p) |
| 2 | { |
| 3 | int i = *((int*) p); |
| 4 | switch (i) { ... } |
| 5 | } |
| 6 | END_OF_FUNCTION(timer_func); |
| 7 | |
| 8 | void 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*) ¶m1, BPS_TO_TIMER(60)); |
| 17 | install_param_int_ex(timer_func, (void*) ¶m2, BPS_TO_TIMER(60)); |
| 18 | } |
That's perfectly valid. Try it out 
[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).
Remember that your function must be fast. If it is fast, then you should have no problems.
I'll assume it's OK then. Thanks, guys!
Oh, well, it's valid, but the address of those two params goes out of scope when the functions return.
A minor detail 
Of course those vars will be global or declared in the main game loop. I missed that in the example above, sorry. 
Remember that your function must be fast.
That's the idea, it'll just update some flags.
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.
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.
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:
| 1 | struct TimerInfo { |
| 2 | int i; |
| 3 | char c; |
| 4 | }; |
| 5 | |
| 6 | void timer_func(void* p) |
| 7 | { |
| 8 | TimerInfo &t = *(TimerInfo*)p; |
| 9 | |
| 10 | switch(t.i) { |
| 11 | case 0: cout << t.c << endl; |
| 12 | } |
| 13 | } |
| 14 | END_OF_FUNCTION(timer_func); |
| 15 | |
| 16 | void 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 | } |
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.
Unless you are making a 3D (especially a FPS), Allegro timers are fine and portable enough.
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.
If anybody had some framework for basic QPC/gettimeofday that can be reused easily, that would be nice.
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.
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.
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 | |
| 7 | bool initialized = false; |
| 8 | |
| 9 | int timer = 0; |
| 10 | int timer_ms = 0; |
| 11 | |
| 12 | #ifdef ALLEGRO_WINDOWS |
| 13 | LARGE_INTEGER last_perf_count_li; |
| 14 | LARGE_INTEGER perf_count_li; |
| 15 | LARGE_INTEGER perf_freq_li; |
| 16 | unsigned long long last_perf_count; |
| 17 | unsigned long long perf_count; |
| 18 | unsigned long long perf_freq; |
| 19 | |
| 20 | unsigned 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 | |
| 28 | bool qpc_mode = false; |
| 29 | |
| 30 | void timer_proc() { |
| 31 | ++timer; |
| 32 | } |
| 33 | END_OF_FUNCTION(timer_proc); |
| 34 | |
| 35 | void 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 | |
| 55 | void stop_timer() { |
| 56 | if (!qpc_mode) |
| 57 | remove_int(timer_proc); |
| 58 | } |
| 59 | |
| 60 | void 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 | |
| 74 | float 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 | |
| 93 | const 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 | |
| 105 | float 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.
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()); }