Hey fellas. So here's the deal. I've got event source and event listener classes. I'm trying to decide if I need a separate thread for each event source, like keyboard, mouse, and so on. Right now I only have a separate thread running the timers, and I'm trying to decide if I should have a separate thread for each input source.
The real problem comes when an event is emitted. If the input is running in a separate thread, and that thread calls the OnEvent method of the listener then it means the users of my listener class will need to synchronize data with mutexes, whereas if it runs on the main thread it would not.
My second problem comes with the use of ALLEGRO_EVENT_QUEUE's. I don't know how to properly emit events when they occur. If I emit events as soon as they are asked for, that works, but defeats the purpose of a listener. If I emit events as soon as they occur, then that means I need a separate thread so it doesn't block processing waiting for events.
So, really, I'm just looking for some advice on how to cleanly design my event system.
Should I stick with listeners and use separate threads? Or accept that some events won't be processed until the user manually checks the event queue?
]]>My question is, why would you want separate threads? Earlier today, I had a subroutine in the timer handler of a loop incrementing an array of 64k floats, and the timer was going 10000 times a second, and it was only using 70% cpu. No extra threads involved. Compared to that, handling a keyboard or mouse event is child's play.
]]>A separate thread is so that the main thread doesn't get blocked waiting for input.
If I don't use threads, and just register everything as an ALLEGRO_EVENT_SOURCE to my event handler, then listeners don't get called until someone asks the queue for data.
]]>A separate thread is so that the main thread doesn't get blocked waiting for input.
That's the whole point of using events, to allow it to continue. Then you pick off the events off the queue as you're ready to handle them. If they're really time-consuming, set a flag to be checked occasionally. Or am I misunderstanding something here?
]]>I am not concerned about cpu use here, but rather the order and timing that events get processed in.
Maybe I'm just getting hung up on the idea of a listener. I would like to allow people to register listeners to my event sources, and that is where the whole source of the problem is coming from.
]]>I don't see why you can't have listeners without separate threads?
]]>If there are no separate threads, then any input coming into the event handler queue has to be polled, and cannot be emitted to listeners until it is actually received, as opposed to emitting an event immediately when it occurs, which requires threads. At least I think that is what my problem is.
]]>I'm not sure why timing is an issue at all, it would be nice to know more about why you need it. Even if you have an event that needs to be dispatched immediately, the only reason this would be useful is if you had many threads for many events.
For example. I would have my main event dispatch thread which might give out a mouse click. then I do:
for each listener { listener->event(e); }
However, unless each listener is on a separate thread, this is completely useless. I can think of very few practical examples where this is useful.
Multithreaded listeners are much less practical than message systems which are multithreaded because listeners, by nature, need to wait until the current listener has returned to give to the next. Thus you are stuck with the polling method:
for each listener { listener->queueEvent(e); }
Which would be non-blocking on the worker threads, and the worker threads poll and execute tasks.
Since I'm inferring this has to do with GUI in some way, the simple solution I would say is, if you have a task that will make the UI unresponsive, fork it to another thread.
//main thread for each listener { listener->timerTicked(e); } //something really long WikipediaLoader::timerTicked(Event e) { createAndJoinNewWorkerThread(&WikipediaLoader::loadAllOfWiki,this); }
This would avoid all the mutex stuff since when wikipedia is loaded, it could simply call WikipediaLoader::finishedLoading(Wikipedia* wiki);
You can always use Boost or C++11 to make mutexes less intimidating. But I do hope you have a compelling reason for such a complex design.
]]>any input coming into the event handler queue has to be polled
al_wait_for_event(queue, &event); //Sleep until something happens
al_wait_for_event(queue, &event); //Sleep until something happens
That's exactly what I mean by polling. You are actively asking the queue not to return until you receive an event. Perhaps I wasn't clear enough earlier.
The alternative is to have another thread monitoring that input (using al_wait_for_event in the backend) which will emit an event to its listeners as soon as an event is received on the queue. That however raises the issue of your listeners callback code executing on multiple threads. This means all my users would have to use mutexes around their code... which would be nice to avoid.
My second alternative is to have queues emit events to listeners when they are queried. It would keep processing on the main thread, but all listeners reactions are delayed until the user processes them. Not my favorite option, but it seems to be what is being suggested.
Since I'm inferring this has to do with GUI in some way, the simple solution I would say is, if you have a task that will make the UI unresponsive, fork it to another thread.
Yes, it is for the main system and event handling of my eagle library.
The only reason I am having this dilemma I belive is because when I did things the way everyone seems to be suggesting, (using ALLEGRO_EVENT_QUEUE's) and registering all the input, but then that means none of the listener callbacks get called until you poll the queue. See what I am saying?
]]>none of the listener callbacks get called until you poll the queue.
The loop that "polls" events doesn't run continuously, it hangs at the al_wait_for_event() line (sleeps) until an event is received. Then it runs through the switch statement full speed.
Try this little console program, which doesn't have a timer. It'll print a line with event count every event it receives, so swirl the mouse around in the window and/or hit keys when it has focus, and watch the printf's in the command prompt window you started it from. Hit ESC to quit.
]]>Exactly what Arthur said. The way any GUI application functions is that it yields the cpu until there is an event. Then, the event is processed. This is not polling. Polling is more like:
void logic() { foreach queued event do event; render(); }
al_wait_for_event blocks and sleeps until the OS interrupts it with an event. So effectively, the first listener will process the event immediately when it happens. It does not have to wait until the next while iteration as you might be thinking.
]]>It does not have to wait until the next while iteration as you might be thinking.
I think it does, since there's a switch statement and all the cases have break statements, but it doesn't take but a few milliseconds at worst (probably doing the call to al_wait_for_event())
]]>I know I was abusing the use of the word poll there, but you do you understand what I'm saying? Calling al_wait_for_event blocks the thread. The question is, should I have a separate thread for handling all incoming events and emit them when they actually occur, or do I have the user acknowledge they need to emit the event or do I simply have my event handler intercept each message and emit them to listeners when the user ahem polls the queue by calling EventHandler::WaitForEvent()?
And what if I had a separate event source? If my event handler is sleeping waiting for an event from al_wait_for_event then I have to wake it up by emitting an event to its own ALLEGRO_EVENT_QUEUE and then I have to ignore it and return the next real event.
I'm just saying, that if I want to have listeners for input sources, then someone needs to be polling for them, whether that is the user or a separate thread is the issue.
Should I show code? Maybe I'm just not explaining well, but this is all for the main system of my library, so I want to design it right before I go and put coding time into it.
]]>I think I get what you mean. You don't want your GUI to force a blocking function like al_wait_for_event to be called. But, I have to wonder, if someone is using A5 but not using al_wait_for_event for their game timer and everything else, I have to wonder what it is they are trying to do with your GUI. I really do not see why any sane programmer would want a straight-up infinite while loop that would not yield the CPU. Even the logic / update function should only be called every 1/60th of a second.
Agui, for example, requires you to supply the Allegro event:
Which has:
Which calls the Agui input handler:
void Scene::processGuiInputEvent( ALLEGRO_EVENT* evt ) { m_a5GuiInput.processEvent(*evt); }
Everything in A5 is event driven and I do not see why anyone would not be okay with the blocking al_wait_for_event.
]]>Really I am saying that I want listeners on my input sources but then they need to run in their own thread. That's all I'm really saying, but that makes it ugly and then you need mutexes, which aren't hard, but most newbs don't understand mutexes.
For instance to get an event from a keyboard input source I would need to have a separate allegro event queue listening to the keyboard running in a different thread. This then means you need to synchronize with the main thread to share data, which sucks.
The crux of the problem is not being able to wait for an allegro event and receive an outside event from an EagleEventSource at the same time without using another thread to wait for allegro events.
]]>I'm wondering though, in what instance would you receive an eagle event that is not triggered by an allegro event? I think Allegro event queues are thread-safe so, from another thread, you could emit an Allegro user event that would be handled by your Eagle API and dispatched on the main thread to listeners.
]]>Here, I'll show you the problem I'm having currently. My timers are an event source, or at least I would like them to be, but if I
1) al_register_event_source them to the event handler's allegro event queue then who do I have emit events to listeners? My event handler class? When? When the user waits for an event? Or when the event happens? Garrh.
void Allegro5Timer::RegisterTimerInput(EagleEventHandler* event_handler) { /* EAGLE_ASSERT(event_handler); Allegro5EventHandler* a5_event_handler = dynamic_cast<Allegro5EventHandler*>(event_handler); EAGLE_ASSERT(allegro_handler); ALLEGRO_EVENT_QUEUE* allegro_queue = a5_event_handler->AllegroQueue(); EAGLE_ASSERT(allegro_queue); al_register_event_source(allegro_queue , al_get_timer_event_source(timer)); */ // so we don't have timers registered to queues and eagle timers registered as event sources at the same time SubscribeListener(event_handler);/// TODO Convert to an event source }
Timers work fine with al_wait_for_event (commented code here) but then no one emits the event, which can be done every time al_wait_for_event returns, but do you have someone constantly monitoring this (another thread)? What if no one ever asks the queue for an event? Then they just pile up, without any of their listener callbacks being called. See my problem?
I think I'm just gonna have separate threads and make listeners use mutexes. IDK. What else can I do?
]]>I might not be understanding correctly again but,
In agui, the user HAS to do the following:
al_wait_for_event(queue,evt); m_a5GuiInputHandler->processEvent(evt));
So Agui gets to see every Allegro event, regardless of whether the user uses the event or not.
It is at this point that you would do something like:
if the event timer is an eagle timer then dispatch event to this timer's subscribers
Using this method though, if someone registers a 10ms timer but the game loop takes 15ms, then you will end up with timer events piling up. However, the only way I can think of solving that would mean that the user timer callback would be called at the correct time from another thread which could introduce issues depending on what the user is doing. But if you have a mutex that blocks the main thread, it should hopefully not cause an issue. (Some issues it could cause though is, say you're in the middle of rendering when the timer ticks, and that timer event causes you to add a gui element thereby invalidating the iterator and crashing the main thread when it returns). So your users will have to be careful if you do it this way.
]]>How does Java do dynamic gui's then? Does their gui run on the EDT? Or do they just use synchronize a lot?
I mean, I could use a delayed execution model, where the listeners are notified as soon as you take the event off the queue. That would mean its your own fault if you don't empty the queue. But that still doesn't tell me what to do with my timer class and trying to make it an event source. It just feels wrong to have a timer class not call its callbacks on an interrupt basis. I can always synchronize the event handler with a mutex, and then I guess I would leave the responsibility of timer listeners to implement their own locking. It's not perfect, but that seems like the best bet so far to me.
]]>I'm not sure how Java does a lot of things TBH. The JVM is like a nice magic box.
Your idea sounds good. However, I would think about exactly what the user will be using these timers for and how much precision they really need.
In Agui, I got around this problem by sacrificing timer precession.
in my logic() loop, I go through each timer and do a timestamp check with al_get_time() which is pretty accurate.
If the time is greater than the interval, then dispatch to the listeners.
This design avoids timers queuing up, avoids mutexes, syncing, and is generally accurate to 1 logic() execution which is 17ms.
I figure if people need to do animation, they'll do it in Widget::logic(double time).
If they need more than 17ms accuracy, they probably are doing something that should use a native OS timer instead.
Something like Java, or QT is a purely event-driven GUI that invalidates windows to redraw, but a game GUI has different goals that can be adapted to a game loop.
]]>Edit 2 :
Important fixes
Eagle5_0pt3pt1_bugs_fixed_plus_event_demo.7z
If you downloaded anything be sure to get the latest version. I forgot to start the event thread, so there were never any messages in the queue! There is now a demo program showing all the events that are being received with the messages on the console and displayed on the screen as well. Please try it out!
Original post
Ok, been looking at some things, and here's the next option : Make the event handler run in its own thread, and protect it with a mutex. So users can access it from any thread they want. And then make listeners lock their own mutexes to protect their own data from corruption if necessary. This way the event handler can have its own dedicated thread to listen for events and yet they can access the queue from any thread they want and it will do the locking for them. Then, I don't need to make every input have its own thread, rather just use al_register_event_source and monitor the event queue.
Edit:
Alright, been working on it, and in addition to the event handler having its own thread where it listens to the allegro event queue, I've added the ability to emit events at once, or later on when they are read from the event handler's queue. That way no one ever need touch a mutex if they don't want to.
I guess code speaks better than words, so here is my allegro 5 event handler :
Allegro5EventHandler.hpp & Allegro5EventHandler.cpp
I'd like to note that this is my first time using a condition variable, and it works well to signal the waiting thread for me.
I'd also like to note there is still that pesky d3d_shutdown crash with my code.
Here : https://www.allegro.cc/forums/thread/613306
and here : https://www.allegro.cc/forums/thread/613372/991308#target
Program received signal SIGSEGV, Segmentation fault. 0x6779fc80 in d3d_shutdown () at C:\downloads\Programming\ProgrammingLibraries\Allegro\5pt1pt8\allegro\src\win\d3d_disp.cpp:2610 2610 _al_d3d->Release(); (gdb) p _al_d3d $1 = (LPDIRECT3D9) 0xc407e0 (gdb) p _al_d3d->Release There is no member named Release. (gdb) p _al_d3d->Release() Couldn't find method IDirect3D9::Release (gdb)
And I've committed to SVN as well if you want the full library.
svn checkout "http://svn.code.sf.net/p/eagle5gui/code/trunk eagle5gui-code"
Source package
Binary + Source package (really unimpressive demo that counts to 16 to demonstrate the use of event handlers and timers, but the library has a fully functional debugging build you can use!)
(They are linked with Allegro 5.1.8 built with MinGW 4.5.0).
(And, with some alteration you should be able to get the CB projects to compile the library for you. Just the matter of fixing some search directories I believe)
So I'd love it if any of you would check out my library. Sorry there are no docs yet. Tell me what your favorite kind of docs are and I might use them.
And if anyone has a clue about the crash in d3d_shutdown, I'd love to hear suggestions.
]]>