![]() |
|
This thread is locked; no one can reply to it.
![]() ![]() |
1
2
|
Callback Problem |
dialmformartian
Member #8,132
December 2006
|
Im new to callbacks, I am trying to make an input buffer with a callback this is what i came up with
Then in main:
but after buf.end reaches 255 the program gets stuck. There are No compile Errors I compile with -lalleg -O -Wall in Dev-C++ |
orz
Member #565
August 2000
|
Change the 255 to 256 in the declaration of message queue. When you write over the 256th element, you're clobbering your control values instead. edit: further commentary: |
dialmformartian
Member #8,132
December 2006
|
Sheesh !! what a silly mistake. I was rather mislead by the complexity thanx a lot. I was also a bit skeptic about the implementation. Are there any other |
axilmar
Member #1,204
April 2001
|
dialmformartian, how do you make sure your main routine does not read a part of the buffer that has invalid values? the 'lock' member does not guarantee you atomic access to the buffer. |
orz
Member #565
August 2000
|
Quote:
I was also a bit skeptic about the implementation. Are there any other I don't know. In my Allegro callbacks, I do not bother locking (A: I think it only matters on DOS, and B: I think it only matters when there is a performance anomaly). Quote: dialmformartian, how do you make sure your main routine does not read a part of the buffer that has invalid values? the 'lock' member does not guarantee you atomic access to the buffer. Hm, yeah, buf.end++ should probably be moved to after the buffer entry becomes valid instead of before, or you have a race condition there. In the example code the race won't actually do anything except cause the wrong values to be displayed once in a while, but for a real-world program that could be a serious issue. |
dialmformartian
Member #8,132
December 2006
|
Yeah like orz said the buf.end++ should move quite fast enough unless the user is faster than the CPU in giving in inputs (does that work that way??). I just wanted to try this after seeing the CGUI source code. CGUI seemed to implement something of the sort of an input buffer, then reads the buffer and issues messages to widgets. I just didn't have the patients to figure how it works exactly. (Can any one help) |
Audric
Member #907
January 2001
|
The current system "gives up" (return and discard the event) when mouse_call() and key_call() race each other. I'd replace the whole "lock" system by a mutex to cover all operations on start and end. The locking and unlocking are only one function call, and when you use the "official" mutex functions it's much easier to verify, from reading the code, that the critical section is indeed protected.
|
axilmar
Member #1,204
April 2001
|
Quote: Yeah like orz said the buf.end++ should move quite fast enough unless the user is faster than the CPU in giving in inputs (does that work that way??). I just wanted to try this after seeing the CGUI source code. CGUI seemed to implement something of the sort of an input buffer, then reads the buffer and issues messages to widgets. I just didn't have the patients to figure how it works exactly. (Can any one help) I used a critical section. I think it would be better for you to use the library pthreads which is compatible with many platforms. |
orz
Member #565
August 2000
|
Quote: dialmformartian, how do you make sure your main routine does not read a part of the buffer that has invalid values? the 'lock' member does not guarantee you atomic access to the buffer.
Quote: Hm, yeah, buf.end++ should probably be moved to after the buffer entry becomes valid instead of before, or you have a race condition there. In the example code the race won't actually do anything except cause the wrong values to be displayed once in a while, but for a real-world program that could be a serious issue.
Quote: Yeah like orz said the buf.end++ should move quite fast enough unless the user is faster than the CPU in giving in inputs (does that work that way??).
Just to make sure we're on the same page here (because it looks to me like we might, possibly, be talking about different things): buf.end++; //Quickly copy stuff buf.queue<i>.type = 0; buf.queue<i>.val = f; buf.queue<i>.ex1 = ex1; buf.queue<i>.ex2 = ex2; buf.queue<i>.ex3 = ex3; to this: buf.queue<i>.type = 0;//Quickly copy stuff buf.queue<i>.val = f; buf.queue<i>.ex1 = ex1; buf.queue<i>.ex2 = ex2; buf.queue<i>.ex3 = ex3; buf.end++; The idea here being that if buf.end is incremented before buf.queue is updated, then your main loop will notice that buf.end is no longer equal to buf.start, and read out the values of buf.queue, at a time when they may or may not be initialized. By making that change, the main loop isn't told about the new mouse event until it's guaranteed to be able to correctly read buf.queue. This is known as a race condition; it is a real possibility on windows, though maybe not on DOS. It would probably be extremely rare, at least on single-core CPUs (the OS scheduler would have to preempt mouse_call after buf.end++ and before buf.lock--). Anyway, onward: Quote: I just wanted to try this after seeing the CGUI source code. CGUI seemed to implement something of the sort of an input buffer, then reads the buffer and issues messages to widgets. I'm not familiar with CGUI, but I am familiar with SDL, which uses an input buffer (patterned after win32 & DirectX I think, though I haven't done much win32 and/or directX programming). It is my opinion that input buffers are superior to the polling interface that Allegro offers for large programs, but inferior for small programs. My current program extends the concept slightly... it timestamps all input, considers time to be an input, and logs all input to file, in order to allow exact program execution to be reproduced, and as a cheap (read: not very good) way to have game mechanics still feel correct when rendering is slow. Quote: I just didn't have the patients to figure how it works exactly. (Can any one help) What's wrong with your current code? It appears to buffer mouse events fine. The keyboard code isn't posted, but I presume it buffers those okay also. What are you asking for beyond general commentary on the concept of input buffers in general and/or the specific implementation in CGUI? |
dialmformartian
Member #8,132
December 2006
|
Hey thanx Orz for clearing the confusion here. Quote: It is my opinion that input buffers are superior to the polling interface that Allegro offers for large programs, but inferior for small programs. My current program extends the concept slightly... it timestamps all input, considers time to be an input, and logs all input to file, in order to allow exact program execution to be reproduced, and as a cheap (read: not very good) way to have game mechanics still feel correct when rendering is slow.
i wanted to create input buffer system because my graph Quote: What are you asking for beyond general commentary on the concept of input buffers in general and/or the specific implementation in CGUI? I just wanted anybody who know about CGUI internals to comment. Quote: What's wrong with your current code? It appears to buffer mouse events fine.
So does that mean i can use this as a reliable buffer system if im not bothered of Quote: The keyboard code isn't posted, but I presume it buffers those okay also. Here it is
and it does work fine. |
Audric
Member #907
January 2001
|
dialmformartian said:
So does that mean i can use this as a reliable buffer system if im not bothered of The only severe problem was the array[256], fixed. Your test code to show the internals is not thread safe, but it will only cause a problem when the buffer is getting full and the buffer's head starts biting its tail. The problem I pointed should not happen often, but consequences on input should be VERY weird: Quote: (May be i could implement mutex later) or should i go for something else like pthread. In my example I took the mutex functions that are in the pthread library, but it doesn't mean you need to multi-thread - the allegro hooks should work fine. |
axilmar
Member #1,204
April 2001
|
Quote: but it doesn't mean you need to multi-thread - the allegro hooks should work fine But allegro hooks are executed by threads other than the main one. I would suggest to dialmformartian to use pthreads anyway. Multi-threading programming is quite tricky and sooner or later he will have untraceable problems. |
Audric
Member #907
January 2001
|
axilmar said: But allegro hooks are executed by threads other than the main one.
Yes, Allegro uses threads internally. |
axilmar
Member #1,204
April 2001
|
Quote: Why dedicate threads yourself if the API already does things right? I did not say that dialmformartian should use his own threads. I said that he should use critical sections to synchronize access to the shared variables because Allegro uses threads. Quote: My first hint was only to use to a reliable and standard mutex system : two functions, lock and unlock. Just surround the critical sections, the closer the better Exactly. |
orz
Member #565
August 2000
|
On further thought: 1. Threading/locking/mutexes/whatever: So far as I can tell, the keyboard/mouse callbacks in Allegro are all executed from a single thread. Thus, there is no benefit to adding mutexes or anything like that. Thus: don't use pthreads/whatever for this. Use of platform-dependant or other-library-specific threading primitives is unnecessary and undesirable in this context. Even the "lock" variable in the code is useless. The only kind of thead-awareness needed is the the stuff alread there - the "volatile" keyword on the buffer variables and careful ordering of when buf.end and buf.start are incremented. 2. mouse_call: dailmformation:
becomes this: buf.queue<i>.ex1 = ex1; buf.queue<i>.ex2 = ex2; buf.queue<i>.ex3 = ex3; becomes this:
2b. Make the size of the buf.queue array be a numerical constant (an enum or #define) instead of hardwired. Do the same thing for the "type" values (currently 0 is mouse, 1 is keyboard - wrap those values in enums or #defined constants instead). That way it's easier to change the values later if there is some need to do so. 3. Buffered Input: dialmformartian: Your main loop reading the callbacks looks correct. I would suggest that that the next step be to add an interface/wrapper to hide/encapsulate the details of the buffer from the main loop or anything else that needs to read keyboard/mouse events. |
Audric
Member #907
January 2001
|
Quote: So far as I can tell, the keyboard/mouse callbacks in Allegro are all executed from a single thread
|
axilmar
Member #1,204
April 2001
|
Quote: So far as I can tell, the keyboard/mouse callbacks in Allegro are all executed from a single thread. Thus, there is no benefit to adding mutexes or anything like that. But that thread is a different one from the main thread. Adding a mutex would prevent synchronization problems between the main thread and the allegro input thread. Quote: You're implementation is perfectly fine as is. Actually, it is not. There is a possibility that non valid data will be read from the buffer. |
orz
Member #565
August 2000
|
But that thread is a different one from the main thread. Adding a mutex would prevent synchronization problems between the main thread and the allegro input thread. Quote: Actually, it is not. There is a possibility that non valid data will be read from the buffer. That was fixed by moving buf.end++. If you are saying that another issue exists, describe it more specifically. |
axilmar
Member #1,204
April 2001
|
Quote: and all writes are atomic. No they are not atomic. A variable increment has 3 CPU instructions: a) load value to register, b) increase value, c) put value back to memory. For example: int x = 0; x++; becomes: 004113B5 mov eax,dword ptr [x] 004113B8 add eax,1 004113BB mov dword ptr [x],eax (copied from the disassembly window of the VS debugger). |
orz
Member #565
August 2000
|
Quote: No they are not atomic. A variable increment has 3 CPU instructions: a) load value to register, b) increase value, c) put value back to memory.
I was not refering to the whole read-modify-write aspect, only the write aspect. ie the value is always either equal to what it is before the statement or after the statement, the write portion is never split into two like it could be for a 64 bit integer. |
axilmar
Member #1,204
April 2001
|
Quote:
I was not refering to the whole read-modify-write aspect, only the write aspect. ie the value is always either equal to what it is before the statement or after the statement, the write portion is never split into two like it could be for a 64 bit integer. But the whole point is to make the whole thing deterministically safe. It's not a big deal to include pthreads in your source anyway. |
Kitty Cat
Member #2,815
October 2002
![]() |
Quote: But that thread is a different one from the main thread. Depends on the system. Allegro can run single-threaded on Linux (AFAIK), and has to run single-threaded on DOS. And given that the current API will be wrapped around a new API in the future, you can't know how that'll do it either. It's not good practice to assume callbacks or timers are run from a seperate thread than your program. -- |
dialmformartian
Member #8,132
December 2006
|
where do i get pthread |
Kitty Cat
Member #2,815
October 2002
![]() |
-- |
orz
Member #565
August 2000
|
Quote: But the whole point is to make the whole thing deterministically safe. It's not a big deal to include pthreads in your source anyway.
Quote: Depends on the system. Allegro can run single-threaded on Linux (AFAIK), and has to run single-threaded on DOS. And given that the current API will be wrapped around a new API in the future, you can't know how that'll do it either. It's not good practice to assume callbacks or timers are run from a seperate thread than your program.
Sigh. Now, the answers for each question: 2. Is this the manner in which Allegro intends its callbacks to be used? 3. What is the cost of adding additional locking? edit: note: this is only considering adding locking to the callback. I would recommend adding locking to any function that reads from the input buffer if and only if your code is multithreaded independent of Allegro. |
|
1
2
|