Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Callback Problem

This thread is locked; no one can reply to it. rss feed Print
 1   2 
Callback Problem
Thomas Fjellstrom
Member #476
June 2000
avatar

1. I don't believe pthreads EXISTS on DOS or DS.
2. You should never do anything clever in allegro timers. ever. You're just asking for trouble.
3. It just won't work on platforms without locking features.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

axilmar
Member #1,204
April 2001

Kitty Cat said:

Depends on the system. Allegro can run single-threaded on Linux (AFAIK), and has to run single-threaded on DOS.

If one is interested in running the program in DOS, then the app must be coded in a special way so as that DOS is handled appropriately.

It is not very difficult to use #ifdef to include different APIs according to platform.

Orz said:

Would adding a pthreads lock to the callback change anything on any specific platform?

Yes. It would make the program correct in those platforms that input is handled by a different thread.

Orz said:

Is this the manner in which Allegro intends its callbacks to be used?

Allegro has many DOSisms. Any modern library would not use the concept of interrupt to service input, but the concept of input queue. Unfortunately for us, Allegro does not do that, so there are two solutions:

1) fix Allegro to provide a mouse queue, like the keyboard queue.
2) use callbacks to implement your own queues.

I have done #2 in my GUI code.

Orz said:

What is the cost of adding additional locking?

It's minimal, because critical sections are used.

Orz said:

In order for it to change something, the callback functions would have to be running concurrently with themselves on some platform.

Nope, you got it wrong here. The lock will not synchronize the input threads, because there is only one input thread. The lock will synchronize the input thread and the main thread.

Orz said:

If you use Allegro's built-in locking mechanisms (_enter_critical() and _exit_critical())... well, I don't know what the drawbacks are - Allegro's built-in locking mechanisms are undocumented so far as I can tell, so I presume you're not intended to use them for normal Allegro programs.

Exactly.

Orz said:

If you use platform-specific locking mechanisms, the obvious cost is in portability.

It's no big deal to do so; just a couple of #ifdefs.

Orz said:

If you use pthreads or another threading API with implementations for a variety of platforms, the obvious cost is the standard cost for adding dependancies - it makes it harder for arbitrary Allegro users to get your code running - they have to get the library themselves, and figure out how to install it and link with it. This cost is greatly reduced if you are the only one likely to compile your code. (additionally, I suspect that using pthreads calls inside a DOS interrupt is a bad idea)

The cost of debugging multithreaded programs far outweights the cost of installing and linking with a library.

Here is a very interesting link with the following quote:

No variable or structure should be accessed from multiple threads without being accessed in a thread-safe fashion.

Obviously the guy has great experience with threads and Windows, so if you don't accept my comment, please accept his.

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

If one is interested in running the program in DOS, then the app must be coded in a special way so as that DOS is handled appropriately.

Not if its coded properly. As long as you use Allegro properly, and don't rely on platform-specific behavior (eg. how callbacks are implemented), it'll work. However, people tend to become lax because "it's not needed right now for my target platform(s)" and thus don't bother with proper code.

Quote:

It is not very difficult to use #ifdef to include different APIs according to platform.

And given that this will be layered on top of the new API in the future, what'll you do if all platforms, instead of just DOS, are then more sensitive to improper code? You'll pretty much lock it out of being able to use the compatibility layer of newer versions and require Allegro 4.2 only.

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

axilmar
Member #1,204
April 2001

Quote:

Not if its coded properly.

The only 'coded properly' technique with multiple threads is to use synchronization. Didn't you read the link I posted?

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

The only 'coded properly' technique with multiple threads is to use synchronization.

Yes, I fully agree with that. I'm talking about Allegro's callbacks, though. :P You can't assume whether Allegro's callbacks are threaded or not.

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

orz
Member #565
August 2000

Quote:

1. I don't believe pthreads EXISTS on DOS or DS.
2. You should never do anything clever in allegro timers. ever. You're just asking for trouble.
3. It just won't work on platforms without locking features.

1. http://moss.csc.ncsu.edu/~mueller/pthreads/
2. I don't consider copying input to a buffer "clever". It's basically the ONLY thing that the input callbacks are useful for.
3. edited: hm... It won't work correctly on platforms with sufficiently weak memory coherency models. Investigating...

Quote:

The cost of debugging multithreaded programs far outweights the cost of installing and linking with a library.Here [flounder.com] is a very interesting link with the following quote:

[bold]No variable or structure should be accessed from multiple threads without being accessed in a thread-safe fashion.[/bold]

You conspicuously omitted the next line "Note that this applies only to variables where concurrent access can produce incorrect results". He then links to an example of his where he recommends using just volatile variables.
You might think that your position is vindicated by his next statement that incrementing a volatile integer is not thread-safe on a multi-CPU system. However that statement does not apply to the special case where only one thread modifies a variable (while other thread(s) read it).

Quote:

Nope, you got it wrong here. The lock will not synchronize the input threads, because there is only one input thread. The lock will synchronize the input thread and the main thread.

There are three different locking scenarios we're looking at here in this thread:
locking type A: input callbacks lock to prevent reentry into the input callbacks
locking type B: input callbacks lock and input reader lock (same lock), to prevent reader from reading not-yet-ready events
locking type C: input readers lock (in a program with multiple threads, not counting those allegro created)

In that particular case, you were apparently talking about type B, while I was talking about type A. It is my position that locking types A and B are pointless. I think everyone is in agreement that type C is necessary if and only if the program is multithreaded (not counting Allegro-created threads that the user code knows nothing about).

However, I'm beginning to suspect that you might be right. After reading through a bunch of x86, PPC, and C docs, it looks like the ordering may not be guaranteed between multiple CPUs under the circumstances in which the callbacks execute on various platforms unless extra effort is made. That's somewhat scary though, as this appears to be what Allegro does internally... possibly I've been up to long, but to my current interpretation, not only is this code slightly flawed in a multi-CPU environment, but so is Allegro keyboard handling on WIN32 and OSX. And I'm not too sure about the Linux version either... executing in the SIGIO handler protects it somewhat, but only if the reading thread is the original thread, and it can also execute in a timer callback instead, or from the main thread if simulate_keypress is called...

Will think more tomorrow. Hopefully.

edit: fixed typo and changed #3 at the top

axilmar
Member #1,204
April 2001

Quote:

Yes, I fully agree with that. I'm talking about Allegro's callbacks, though. :P You can't assume whether Allegro's callbacks are threaded or not.

Well, if the game/library needs to behave correctly in the 90% of the computers out there (i.e. those that run Windows), you need to code threads properly.

Quote:

You conspicuously omitted the next line "Note that this applies only to variables where concurrent access can produce incorrect results". He then links to an example of his where he recommends using just volatile variables.
You might think that your position is vindicated by his next statement that incrementing a volatile integer is not thread-safe on a multi-CPU system. However that statement does not apply to the special case where only one thread modifies a variable (while other thread(s) read it)

Incrementing an integer is not thread-safe, period. If you read the whole thing, you would have seen the analysis of the assembly code.

You are just asking for trouble especially when the integer is an index to a buffer.

Quote:

However, I'm beginning to suspect that you might be right.

No sh1t, Sherlock! :-)

CPUs have gone multicore, you have forgotten that???

orz
Member #565
August 2000

Quote:

Incrementing an integer is not thread-safe, period. If you read the whole thing, you would have seen the analysis of the assembly code.

You seem to be ignoring the point of that: when a variable is only written to by one thread, the atomicity of the entire read-modify-write operation becomes irrelevant. Only the atomicity of the write operation itself is relevant, because the variable cannot be modified asynchronously to that code (because only one thread modifies it). And the write is generally atomic, provided that the integer is aligned (caveats: not true on 386sx when paired with a 32 bit compiler, some 8 bit CPU/compiler combinations, a few other arcane places... but to get even a remotely possible failure you have to find a platform that uses one of the handful of CPUs on which that's not true, uses multiple CPUs concurrently, and runs Allegro programs; you'd have an easier time finding a system that uses 10 bit bytes).

Quote:

You are just asking for trouble especially when the integer is an index to a buffer.

That is the part I'm agreeing with now - that using the variable as an index into a changing buffer is unsafe. According to my current understanding, this code has the capacity to return garbage input (though it almost never will). It could be fixed with threading libraries, platform specific locking mechanisms, or more exotic coding methods. However, I think that the Allegro internals have the same flaw in them, so you suffer from this flaw regardless...

Quote:

CPUs have gone multicore, you have forgotten that???

I remembered the multicore part, but did not realize that Intel and others had weakened their memory coherency policies (I expected at least PRAM consistency for writes to volatile variables). And I presumed that Allegro would arrange things in such a way that its input callbacks were usable in a safe manner without extra dependencies or extraordinary effort.

edit:

ARRGHH!
Okay, I was too tired when coming to the conclusion that it wasn't safe. x86 DOES still offer strong memory coherency, I misread the docs as saying the opposite of what they said. Here's my current position (hopefully I will slow down the rate at which I change positions soon):

The code is correct on some platforms only.
Allegro code is correct on some platforms only.
Which platforms?

x86/*: Allegro and this code both work, because of strong memory coherency guarantees on normal x86 systems.

non-x86/linux: Allegro and this code probably work correctly, if the user code is totally single-threaded. Merely having only one thread talk to allegro is not enough though - it must also be the thread that handles signals. On x86 specifically the issue is (I think) irrelevant though because of the x86/* strong coherency guarantees. However, the buf.lock mechanism should be kept, because in (very rare) cases the callback can become reentrant.

PPC/macOSX: Allegro and this code are both incorrect on this platform.

PPC/macOS9 and x86/DOS: Allegro and this code are correct, because (I think) the code all executes in interrupt context anyway.

axilmar
Member #1,204
April 2001

Quote:

That is the part I'm agreeing with now - that using the variable as an index into a changing buffer is unsafe.

Quote:

ARRGHH!

You went into all this trouble just to prove what others said from the beginning? :-)

orz
Member #565
August 2000

Quote:

You went into all this trouble just to prove what others said from the beginning? :-)

And to submit a bug report : )

http://www.allegro.cc/forums/thread/590120

 1   2 


Go to: