Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Simple programs that collect data from only one system need only one (blocking) event loop. For example, Windows applications have the message loop, POSIX network programs usually have a select/epoll/etc. loop at their core, pure SDL games use SDL's event loop. But what if you need to collect events from several subsystems? Such as an SDL game which doesn't use SDL_net for networking.

I can think of several solutions:

  1. Polling (ugh)
  2. Put each event loop in its own thread, and:

    1. Send messages to the main thread, which collects and processes the events, or
    2. Place the event-processing code of each thread in a critical section, so that the threads can wait for events asynchronously but process them synchronously
  3. Choose one subsystem for the main event loop, and pass events from other subsystems via that subsystem as custom messages (for example, the Windows message loop and custom messages, or a socket select() loop and passing events via a loopback connection).

Note that I'm talking about an event loop that blocks execution until an event arrives. This allows writing event-based games that don't pointlessly burn 100% CPU, and can process events even during drawing or performing a logic update. Checking if a subsystem has events and continuing execution otherwise is what I'd call polling.

Option 2.1 is more interesting on platforms where message-passing is a well-developed threading primitive (e.g. in the D programming language), but 2.2 looks like the best option to me.

share|improve this question
    
How does 2.1 differ from 3? It would appear that 2.1 is the simplest case of 3, merely designating the system on the main thread as the authority. –  Kylotan Feb 19 '11 at 20:04
    
Hi. I explained that in a comment to homebrew's answer. –  CyberShadow Feb 19 '11 at 21:32

4 Answers 4

up vote 1 down vote accepted

So, the tech that I'm working with right now has a notion of a central message stream shared by all threads via subscription and duplication (threads install handlers which may or may not duplicate consumed messages).

This stream is fed by a thread dedicated to translating/polling different event queues/devices. If need be, because event sources all expose the same API to this thread, I can (in, say, the case of XInput which seems to be polling-only) create a class containing a thread devoted to handling a particular sort of input; this lets me handle things as diverse as 360 controllers and asynchronous social media querying.

One nice thing about having this unified stream is also that I can do weird things like have a handler that consumes events and pattern matches them with a state-machine to handle things like attack combos for a fighting game and then emit a game event that says "Haruken!"

Keep in mind that the end goal for something like this is to combine a diverse range of input events into a single stream some tech is guaranteed to handle correctly (Quake had a good take on this).

share|improve this answer
1  
To amend, an event stream for an application shouldn't even be a thread, ideally; it should just be a queue (lockless or protected with mutices) that external threads manipulate. We use a dedicated thread just to open up the option of strange hacks. –  ChrisE Feb 18 '11 at 22:49
    
That sounds like a lot of threads. My plan is to use a single thread which handles all events, and defer slow processing to separate threads (task completion is signalled as another event). –  CyberShadow Feb 19 '11 at 1:16
1  
We're a pretty heavily threaded engine (targeting PCs, so this is acceptable). One thread is used to service normal event loops, one to do rendering (with the way OpenGL handles contexts, this is somewhat necessary), and then one more to handle logic, feeding some number of worker threads, and so forth. –  ChrisE Feb 19 '11 at 21:16

I remember looking through the code base of ChucK http://chuck.cs.princeton.edu/ and noticing that the whole program uses just one thread. ChucK is an interesting example because it fakes concurrency using one thread in a "strongly-timed" model (that's my best explanation ;) ). However, there are threads for different event sources (MIDI events, Open Sound Control events, or socket messages) and these queue the events for processing by the main loop.

So...I'd pick option 2.1. which seems the same as option 3.

share|improve this answer
    
Thanks. The difference between options 2.1 and 3 is that all the main thread does in 2.1 is collect messages sent by other threads, using native thread message-passing. 3 is simpler, but more of a hack since you [ab]use another subsystem to send messages to yourself. –  CyberShadow Feb 18 '11 at 22:09

Adding threads to the system creates bugs at worst and inefficiency at best (due to the necessary synchronisation) unless you have a clear example of the parallelism helping, so I wouldn't use it in this case. Note that your option 2.1 has left out the synchronisation aspect - you're ok on Windows since PostMessage is synchronised, but this is something you need to check on a per-API basis.

I wouldn't choose to route several subsystems into another via custom messages because this could require significant rewriting should you change that subsystem - eg. port from Win32 to SDL or whatever.

So, I usually go with polling where the subsystems make it possible and efficient. eg. It's rarely a problem to poll your network library, because either there is data ready to read or there is not - you're not going to slow down your program by checking for it if the library is well made.

share|improve this answer
    
Sorry, I think this is a mis-informed answer. I don't see where any synchronization problems could appear on 2.1, unless your platform's thread messaging is poor/nonexistant and you need to write your own thread-safe event queue (and even then this couldn't have anything to do with PostMessage etc.) "Significant rewriting" is not a problem unless you're Doing It Wrong - remember that you are communicating with code within the same process, so you can easily just send a pointer to a subsystem-agnostic structure with the data. –  CyberShadow Feb 19 '11 at 21:12
    
I also can't make any sense of your explanation of how polling can ever be efficient. You have to sprinkle your event polling at arbitrary point(s) in your program's main loop (for example, after drawing a frame, which is sadly what most games do). This is often "good enough" in practice, but it's by far not the best solution. –  CyberShadow Feb 19 '11 at 21:15
    
Not being able to process input or handle any other kinds of events "between frames" is a real, practical problem that's been pointed out by users of the game I'm maintaining. Even though the game's physics engine runs at 50FPS and it's not graphically heavy, its players have become so skilled that they're hitting the limitations of per-frame input, which are preventing them from expanding their skills further. It's a problem which I intend to get rid of (along with decoupling logic and video frame rate). –  CyberShadow Feb 19 '11 at 21:23
1  
@CyberShadow: They can, but they won't get any feedback on it. Even if they push the button perfectly, you can't render the frame or do the physics to actually update that action in time. –  DeadMG Feb 20 '11 at 11:07
1  
@CyberShadow: By default Windows won't switch between threads more rapidly than every 15/16ms or so, unless you change it with timeBeginPeriod. Other operating systems are similar. Context switches are expensive so most OSes try to minimise them. As for the D thread messaging, that may be fine but there is nothing like that as part of C++. There is also nothing like that in SDL. To compare pros and cons further will require a concrete example. –  Kylotan Feb 20 '11 at 14:03

If the event loop is cleanly written, there is no reason that a primary thread cannot have two event loops. Windows, for example, has PeekMessage. First you process all Windows messages, then you move on and process all whatever messages. As long as you're not blocking on a call to receive a message, then there's no reason that you can't call two message querying functions in a single loop instead of one.

share|improve this answer
    
That's what I meant by "polling", basically. You can't gracefully wait until an event comes from either event source. It's quite inefficient. –  CyberShadow Feb 19 '11 at 21:07
    
@CyberShadow: No game loop blocks until a new message is available, they go render a new frame or do a game logic update. –  DeadMG Feb 19 '11 at 21:45
    
Oh, I see what you mean now. Please see my comments to Kylotan's answer why I don't consider this a good solution. I'll amend my question to make this clearer. –  CyberShadow Feb 19 '11 at 21:50

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.