LOCK_FUNCTION / LOCK_VARIABLE
spellcaster

Is it save to call these macros several times?

Derezo

All it does is this.

Quote:

This function may be called more than once for a given page; the DPMI host maintains a lock count for each page.

Note that it uses different methods on different platforms.. so I'm not sure if it does exactly that on all platforms.

I'd recommend keeping track of whether or not you've done it anyways, though.

Sirocco

I don't see why not. I'm assuming that if the memory is already locked, at worst it'll return a failure value. Generally speaking, I know everything I'll need to lock at run time, so I've never actually ran into this particular situation.

ReyBrujo

Wondering, why would that happen? You lock variables only once. I don't believe it will return a failure, because technically speaking, you asked to lock the memory, and the memory was already locked.

spellcaster

The question is: Do I need to keep track of the fact that I've locked the var, or is it ok to lock it several times?

It's not a real problem, it would be pretty easy to avoid it, but it would require some work. If there's some sort of UNLOCK_XXX macro, it would be ok as well.

I've several states, the init rule of the state does all the init stuff (suprise, heh), the done rule clears up. I lock the var in the init rule, but since I can't unlock it in the done rule it's possible that the same var gets locked several times.

Derezo
Quote:

Do I need to keep track of the fact that I've locked the var, or is it ok to lock it several times?

You don't need to keep track, and it is ok to lock it several times.

There shouldn't be a reason you lock it several times, though. Initialization on locked variables should only occur once.

It's like setting a graphics mode. You can do it more than once, but that'd just be silly.

That's just coding practice, though..

[edit: btw, you can unlock memory, but allegro doesn't have a macro to do it.]

ReyBrujo

I believe if you lock something several times, if it throws errors, you would not matter them. As long as it does not throw an exception, you should be able to recover from the error.

Derezo

My bad, allegro DOES have unlock macro's.

#define LOCK_DATA(d, s)             _go32_dpmi_lock_data(d, s)
#define LOCK_CODE(c, s)             _go32_dpmi_lock_code(c, s)
#define UNLOCK_DATA(d,s)            _unlock_dpmi_data(d, s)
#define LOCK_VARIABLE(x)            LOCK_DATA((void *)&x, sizeof(x))
#define LOCK_FUNCTION(x)            LOCK_CODE((void *)FP_OFF(x), (long)FP_OFF(x##_end) - (long)FP_OFF(x))

UNLOCK_DATA((void*)&var, sizeof(var)) is what you'd want for that, I bet..

By the looks of it that's only for variables..

spellcaster
Quote:

There shouldn't be a reason you lock it several times, though. Initialization on locked variables should only occur once.

Well, not really. View it like locking the screen, since this is a pretty good analogy: You lock the screen, do your stuff and unlock it.

The problem is, that I can't unlock the variable. There's no symetric function/macro for the LOCK_XXX macros.

Quote:

It's like setting a graphics mode. You can do it more than once, but that'd just be silly.

Unless you want to switch to a different gfx mode or color depth, eh?

Quote:

That's just coding practice, though..

Well, maybe you can tell me how you'd handle this? I'm always willing to learn :)

Right now the only way I can think of would be to store the fact that I've locked the variable in a state variable.

init-rule: 
    if (varLocked is not true) {
        lock(var);
        varLocked = true;
    }

What'd I'd prefer would be:

init-rule: lock(var)
done-rule: unlock(var)

That would keep everything symetric.

EDIT: You're too fast :)

Derezo
Quote:

Well, maybe you can tell me how you'd handle this? I'm always willing to learn :)

1#include <allegro.h>
2#include "gui.h"
3#include "platformer.h"
4#include "timers.h"
5#include "graphics.h"
6#include "tiles.h"
7 
8int fpsCounter;
9int fps;
10int ms, sec, min, hr;
11int logicUpdates;
12 
13void LogicTimer(void)
14 {
15 logicUpdates++;
16 }
17END_OF_FUNCTION(LogicTimer);
18 
19void ClockTimer(void)
20 {
21 ms++;
22 if(ms >= 100)
23 {
24 ms = 0;
25 sec++;
26 if(sec >= 60)
27 {
28 sec = 0;
29 min++;
30 if(min >= 60)
31 {
32 hr++;
33 }
34 }
35 }
36 }
37END_OF_FUNCTION(ClockTimer);
38 
39void FPSTimer(void)
40 {
41 fps = fpsCounter;
42 fpsCounter = 0;
43 }
44END_OF_FUNCTION(FPSTimer);
45 
46int InitTimers()
47 {
48 LOCK_FUNCTION(LogicTimer);
49 LOCK_FUNCTION(ClockTimer);
50 LOCK_VARIABLE(ms);
51 LOCK_VARIABLE(sec);
52 LOCK_VARIABLE(min);
53 LOCK_VARIABLE(hr);
54 LOCK_VARIABLE(logicUpdates);
55 LOCK_VARIABLE(fps);
56 LOCK_VARIABLE(fpsCounter);
57 
58 install_int(LogicTimer,30);
59 install_int(ClockTimer,10);
60 install_int(FPSTimer,1000);
61 
62 return 1;
63 }

I call InitTimers(), done. Never touch any of that again.

Those variables never get touched ever again. No unlocking, no locking, just reading. With the case of logicUpdate, I subtract one each time I make a logic update. With fpsCounter, I add one each time I draw a frame.

Note: Variables need to be initialized to 0.

Quote:

Unless you want to switch to a different gfx mode or color depth, eh?

Where locking variables is concerned, you can't switch the way you do it. So I think the analogy still stands, but I mean setting it to the same thing. Changing it from 640x480x16 to 640x480x16 :P

Richard Phipps

I do it the same as Derezo. Never had any problems. :)

spellcaster
Quote:

I call InitTimers(), done. Never touch any of that again.

Well, that's great. Now, please tell me how you do it with the state machine I've described? :)

I choose the easy way and added a watcher for the locked state.
Just to get some "OH NOW! EVIL MACROS!" shouts, here're my new timer macros:

1#define TIMER_DECL(VAR) \
2 volatile int VAR = 0; \
3 int VAR##isLocked = 0; \
4 static void VAR##Updater(){ \
5 VAR++; \
6 } END_OF_STATIC_FUNCTION(VAR##Updater)
7 
8#define TIMER_SET(VAR, FPS) \
9 if (!VAR##isLocked) { \
10 LOCK_FUNCTION(VAR##Updater); \
11 LOCK_VARIABLE(VAR); \
12 VAR##isLocked = 1; \
13 } \
14 install_int_ex(VAR##Updater, BPS_TO_TIMER(FPS))

Derezo
Quote:

Now, please tell me how you do it with the state machine I've described?

I don't understand the difference. You're using the same variables each time, so it should be no different.
Lock them once in a single swoop (such as after calling install_timer), and never touch them again. Locking variables is not something you should even have to consider doing more than once during a program's execution.

Set it and forget it!™

Evil macro's work, too, though ;D

Oscar Giner

Will you declare a lot of variables to be timer incremented? Because allegro has a low limit on number on timer ints. I think it's a total of 16 (and some subsystems already take some of those).

In this case, I'd only install 1 high resolution timer and update all variables in this timer.

spellcaster
Quote:

I don't understand the difference. You're using the same variables each time, so it should be no different.
Lock them once in a single swoop (such as after calling install_timer), and never touch them again. Locking variables is not something you should even have to consider doing more than once during a program's execution.

The difference is that each module is like your program.
If you call your program trice in a row, you rely on the fact that the OS clears the system every time.
If you don't have that luxury, you need to take care of it yourself.

Kitty Cat

The LOCK_* macros are only applicable in DOS. And I think the fact that Allegro tries to unlock data before freeing it (in the case of pointers) means that the data should be unlocked. However, I don't think this applies to variables since Allegro never states anyewhere that you should unlock variables before closing the program.

So I think you only need to worry about unlocking if you're locking a piece of malloc'd memory that will be free'd.

Gideon Weems

What is the ## notation in spellcaster's macros? It seems to concatenate VAR as an identifier. If so, I like!

Thomas Fjellstrom
Quote:

The difference is that each module is like your program.

1program_init {
2 foreach module {
3 module->init();
4 }
5}
6 
7program_run {
8 // selected_module->start();
9 selected_module->run();
10 // selected_module->end();
11}
12 
13program_exit {
14 foreach module {
15 module->exit();
16 }
17}

Something like that anyhow.

spellcaster
Quote:

What is the ## notation in spellcaster's macros? It seems to concatenate VAR as an identifier. If so, I like!

It's the concat operator. There's also a string operator #

Quote:

program_init {
foreach module {
module->init();
}
}

program_run {
// selected_module->start();
selected_module->run();
// selected_module->end();
}

program_exit {
foreach module {
module->exit();
}
}

Yep. Now, imagine that these modules are dynamic.

This code shoulnd't leak resources:

while (1) {
    Object foo = new Object();
    delete foo;
}

In the same way, these calls shouldn't leak resources

while(1) {
    state_init(state);
    state_done(state);
}

Agreed?

Thomas Fjellstrom

Uhhh...

while(1) {
    state_init(state);
    state_done(state);
}

add a few states. Like "start" and "end" leave "init" and "done" for the stuff that CAN'T be done over and over. Which is what I was "trying" to say with the last bit of code.

spellcaster

Well, init() is the constructor. done() the destructor :)
What you're doing is to design-by-contract. You force the user (or yourself) to call those routines only once.
If you have start and init what you do is to force the player to keep the state in mind. he has to make sure that start or init (just from the name, I can't even tell what method should be called only once) is called just once.
So, all you do is delegating the responsibilty to the user. I consider this to be rude.

You should be able to create and free a state without wasting resources. I think we agree here?
And if I have to waste resources, it should be the responsibility of the state to ensure that the least amount possible is wasted.

With your approach, you either have to know all states at compile time (so no data driven aopproach is possible) or the user has to keep book whether he has called the "once in a life time method" or the state has to have a "isStartNeeded()/isInitNeeded()" the user can call... or it simply does the check itself - which is my current solution :)

What you're trying to do will result in exactly the same thing as I do. But you force the user to do the work, my code does it automatically. So I'm not sure I see your point.

Thomas Fjellstrom

What wasted resources? Its Locking a variable ::)

spellcaster

Well, that's the point. Nobody was really quite sure whether it was save to call it sveral times or not, and what happens if it's called several times.
But there should be a symetric call to unlock it.
With your point of view, there's either no need at all to worry (then most of your posts in this thread don't make much sense any more ("why having a once in a programs-lifetime-method if it doesn't matter if it's called twice)).
With my point of view, stuff that is locked should be freed, since I don't know what the lock call does. It might differ from platform to platform, etc.

Thomas Fjellstrom

Except that its a legacy hold over from the DOS code, and really isn't required in the other platforms :o

spellcaster

Well, it's not yet depricated. If it's not required at all, then most of this discussion was pretty pointless :)

Thread #397684. Printed from Allegro.cc