SDL events and threads on Win32

I believe I have found the problem I was having with events
and threads on Win32. The documentation isn’t really clear
what should work and what should not.

Could someone confirm my analysis, or point out
any errors?

At present, SDL creates a single window, in some thread.
Windows queued messages are queued on a per thread basis.

SDL uses simplified Win32 calls to process messages,
these calls process messages for the current thread.

The native event handling code itself isn’t run in
a separate thread on Win32. Also, user queries
are not asynchronous – SDL_WaitEvent etc call
SDL_PumpEvents which invokes the native code directly.

Thus, trying to call SDL_WaitEvent in a separate thread
doesn’t work – it’s trying to read events for a thread
which doesn’t own a window and so won’t get any windowing
events.

For my purpose at least this is a serious problem,
events are asynchronous and must be processed asynchronously
and without user polling. Whilst I can hack a fix to my
wrapper code by busy waiting, it may be possible to solve
this problem properly, in SDL itself.

As I understand it, the SDL model is ONE window,
ONE application, multiple threads. Thus from SDL’s
viewpoint SDL threads should all be associated with
events from that one window – there’s no ambiguity,
as there would be in general.

Windows allows reading another threads events,
so if a record is kept of the thread which creates
the window, then any thread can read the events.
Events can also be sent across thread boundaries.

I have no idea if the API is sustained using DirectX though.

Is there any interest in fixing SDL events so they work
in separate threads on Win32? Anyone see any problems
doing it?–
John Skaller
Felix, successor to C++: http://felix.sf.net

Why not only use one event handler and poll/wait for events in a main loop?
if you are already passing something to the thread, create a struct/class to
hold what you are sending, and the event jibberish…the events will point
the main loop event variable…

struct threadpass
{
void* whatever;
SDL_Event *events
};

threadpass example;

example.whatever = (void *)variable;
&example.events = &mainevents;

SDL_CreateThread( &funcname, &example );

Alex~On 2/9/06, skaller wrote:

I believe I have found the problem I was having with events
and threads on Win32. The documentation isn’t really clear
what should work and what should not.

Could someone confirm my analysis, or point out
any errors?

At present, SDL creates a single window, in some thread.
Windows queued messages are queued on a per thread basis.

SDL uses simplified Win32 calls to process messages,
these calls process messages for the current thread.

The native event handling code itself isn’t run in
a separate thread on Win32. Also, user queries
are not asynchronous – SDL_WaitEvent etc call
SDL_PumpEvents which invokes the native code directly.

Thus, trying to call SDL_WaitEvent in a separate thread
doesn’t work – it’s trying to read events for a thread
which doesn’t own a window and so won’t get any windowing
events.

For my purpose at least this is a serious problem,
events are asynchronous and must be processed asynchronously
and without user polling. Whilst I can hack a fix to my
wrapper code by busy waiting, it may be possible to solve
this problem properly, in SDL itself.

As I understand it, the SDL model is ONE window,
ONE application, multiple threads. Thus from SDL’s
viewpoint SDL threads should all be associated with
events from that one window – there’s no ambiguity,
as there would be in general.

Windows allows reading another threads events,
so if a record is kept of the thread which creates
the window, then any thread can read the events.
Events can also be sent across thread boundaries.

I have no idea if the API is sustained using DirectX though.

Is there any interest in fixing SDL events so they work
in separate threads on Win32? Anyone see any problems
doing it?


John Skaller
Felix, successor to C++: http://felix.sf.net


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl


Smith: "…Why, Mr Anderson, Why - Do - You - Persist?"
Neo: “Because I choose to.”
-Matrix Revolutions

Because there is no main loop :)On Thu, 2006-02-09 at 07:54 -0500, Alex Barry wrote:

Why not only use one event handler and poll/wait for events in a main
loop?


John Skaller
Felix, successor to C++: http://felix.sf.net

skaller wrote:> On Thu, 2006-02-09 at 07:54 -0500, Alex Barry wrote:

Why not only use one event handler and poll/wait for events in a main
loop?

Because there is no main loop :slight_smile:

I just fail to see how this is possible even in the best case where
you don’t have to poll (fastevents.c See excellent article by Bob Pendleton).
In every program with a GUI I have ever seen there was a main loop either
implicit or explicit.

.bill

skaller wrote:

Why not only use one event handler and poll/wait for events in a main
loop?

Because there is no main loop :slight_smile:

I just fail to see how this is possible

Well, I was going to explain, but thought better of it,
since the explanation is going to be a bit long winded
and read like an advertisement … but since you ask …

… there is of course a loop. However the
loop is way out of reach of the end user, it’s buried
inside what amounts to a user space operating system.

The mainline code for the Nehe tutorial number 5 looks
like this:On Thu, 2006-02-09 at 17:59 +0200, Vassilis Virvilis wrote:

On Thu, 2006-02-09 at 07:54 -0500, Alex Barry wrote:

===========================================================
/* LINEAR CONTROL MODEL: CANNOT DEADLOCK
~~> async/sync connection
–> sync/sync connection

SDL_event ~~> dispatcher
–> resize handler
–> active handler
–> key handler
timer ~~> framerate --> draw
*/

/* make our communication channels */
var keyboard = mk_schannel[SDL_keysym] ();
var active = mk_schannel[SDL_ActiveEvent] ();
var resize = mk_schannel[SDL_ResizeEvent] ();
var clicks = mk_schannel[int] ();
var rotation = mk_schannel[int] ();

/* start up the fthreads and plug them together */
spawn_fthread { dispatch_event (keyboard, active, resize); };
spawn_fthread { resizechan resize; };
spawn_fthread { activechan active; };
spawn_fthread { keychan keyboard; };

spawn_fthread { drawchan (clicks, the Drawing); };
spawn_fthread { framerate (clicks, 0.1); };
spawn_fthread { execute (rotation, the rotate); };
spawn_fthread { framerate (rotation, 0.1); };

// main thread hangs

There is no loop here!

As you can probably guess, even without knowing Felix,
we’re spawning synchronous threads which communicate
via shared memory and channels.

As a graphics person you will understand this procedure:

==========================================
/* draw the scene */
proc draw(drawing: 1->0) {
if isActive call drawGLScene( drawing );
}

proc drawchan(x:schannel[int], drawing:1->0)
{
whilst true do
var &k : int <- read x;
draw drawing;
done;
}

where the drawGlScene procedure is more or less
the same as Ti Leggetts SDL port of the Nehe tutorial #5.

However UNLIKE the tutorial, this system doesn’t hog the
CPU drawing as fast as possible. Instead it uses this:

======================================
/* write ticks at the desired framerate */
proc framerate (x:schannel[int], framerate:double)
{
whilst true do
Faio::sleep framerate;
write (x,1);
done;
}

to write ticks down a channel, with the specified
period. Recalling the mainline:

spawn_fthread { drawchan (clicks, the Drawing); };
spawn_fthread { framerate (clicks, 0.1); };

you can now see we’re running a 10 FPS framerate.
The timer – an asynchronous event source – is driving
the graphics routine without visible callbacks or
master event loops. Sure … there is a loop … but
it blocks READING the event!

Note that none of this code knows anything at all about
the keyboard or mouse. This is handled the same way:
by plugging fthreads together with channels.

The model is much the same as reading stuff from
files in C. Would you really like to supply a callback
when you open a file, which accepts each character?

I won’t show the other code, but note that the framerate
procedure blocks on an asynchronous event: the firing
of a timer. Similarly, the SDL event reader should also
block on an asynchronous event source.

So, where is the loop? There is no main loop in the Felix
code, as I said. The code that schedules the routines of
course has a loop, but that scheduler is general, and
doesn’t – and can’t – know anything about SDL.
In fact in the standard linkage model, Felix generates
DLLs which are loaded dynamically as plugins.

It is of course possible to write an event fetcher which
uses SDL_PollEvent, and I have done that to work around
the problem. However it is ugly, since it MUST use
an asynchronous event source that works – in this case
I used a busy/wait loop using a timer, similar to
the framerate procedure.

The system is set to revolutionise game programming –
IMNSHO of course :slight_smile:

Synchronous threads are easy to compose (plug them
together with channels). Callbacks cannot be composed.
Callbacks are evil. But event driven programming is
fast, and games need speed.

Felix gives you both. Your threaded code is control
inverted by the Felix compiler into a fully event driven
system. Modular code AND blinding speed. Oh, and the bindings
are super lightweight. Here is the binding for SDL_WaitEvent:

fun SDL_WaitEvent: ptr[SDL_Event] -> int;

where ptr[] means ‘C pointer to’. There’s no runtime glue
code at all – Felix uses C/C++ object model. So it is usually
pretty easy to use your existing libraries … in this case SDL.


John Skaller
Felix, successor to C++: http://felix.sf.net

skaller wrote:

skaller wrote:

Why not only use one event handler and poll/wait for events in a main
loop?

Because there is no main loop :slight_smile:

I just fail to see how this is possible

[snip most of a nice informative article]

I haven’t heard about felix. Looks nice…

spawn_fthread { dispatch_event (keyboard, active, resize); };
spawn_fthread { resizechan resize; };
spawn_fthread { activechan active; };
spawn_fthread { keychan keyboard; };

spawn_fthread { drawchan (clicks, the Drawing); };
spawn_fthread { framerate (clicks, 0.1); };
spawn_fthread { execute (rotation, the rotate); };
spawn_fthread { framerate (rotation, 0.1); };

Some how threads are scheduled when (block until) their
argument changes? (keyboard, active, resize, clicks, rotation)?
These are the channels you are referring to? They look magic^H^H^H^H nice

There is no loop here!

Yes there is! You are just hiding it very well :slight_smile:

[snip]

you can now see we’re running a 10 FPS framerate.
The timer – an asynchronous event source – is driving
the graphics routine without visible callbacks or
master event loops. Sure … there is a loop … but
it blocks READING the event!

That’s the right thing to do.

I won’t show the other code, but note that the framerate
procedure blocks on an asynchronous event: the firing
of a timer. Similarly, the SDL event reader should also
block on an asynchronous event source.

I still believe that you have to take a look at fastevents by
Bob Pendleton to see how to implement a non polling
loop for SDL events

So, where is the loop? There is no main loop in the Felix
code, as I said. The code that schedules the routines of
course has a loop, but that scheduler is general, and
doesn’t – and can’t – know anything about SDL.
In fact in the standard linkage model, Felix generates
DLLs which are loaded dynamically as plugins.

It is of course possible to write an event fetcher which
uses SDL_PollEvent, and I have done that to work around
the problem. However it is ugly, since it MUST use
an asynchronous event source that works – in this case
I used a busy/wait loop using a timer, similar to
the framerate procedure.

Hmm… looks like the approach of fastevents. I am not sure.
However I think it’s not possible to block indefinitely wait
user input (event) because SDL doesn’t share the event queue
with the input subsystem. For example SDL has its own queue,
which is different form the X queue or the Windows queue.
In order to implement timers you are possibly have also
your own queue on top of the SDL queue. I think that the
problem is you have to wake up once in a while in order to
give the chance to the lower levels to fill up their queues…

The system is set to revolutionise game programming –
IMNSHO of course :slight_smile:

Noble goal for sure and Felix.

.bill> On Thu, 2006-02-09 at 17:59 +0200, Vassilis Virvilis wrote:

On Thu, 2006-02-09 at 07:54 -0500, Alex Barry wrote:

I haven’t heard about felix. Looks nice…

:slight_smile:

spawn_fthread { drawchan (clicks, the Drawing); };
spawn_fthread { framerate (clicks, 0.1); };
spawn_fthread { execute (rotation, the rotate); };
spawn_fthread { framerate (rotation, 0.1); };

Some how threads are scheduled when (block until) their
argument changes? (keyboard, active, resize, clicks, rotation)?

Its the same as reading and writing files. The channels are
clicks and rotation in the above. The fthreads don’t have names
in this example: { … } is an anonymous procedure.

There is no loop here!

Yes there is! You are just hiding it very well :slight_smile:

There’s no loop in the Felix code above. There are loops in the
C++ code that schedules the fthreads. There are also loops
in your favourite Operating System doing the same thing.

Sure … there is a loop … but
it blocks READING the event!

That’s the right thing to do.

I think so too …:slight_smile:

However I’d like to prove it, which is best done
by simply writing demo programs and letting people make
up their own minds.

however there is a simple fact which is a killer argument :slight_smile:
Algorithmic code can implement callbacks trivially by simply doing

loop: 
	event = read(); 
	callback(event);
	goto loop;

Therefore, reading events is superior to callbacks
because it subsumes callbacks as shown.

I still believe that you have to take a look at fastevents by
Bob Pendleton to see how to implement a non polling
loop for SDL events

I have. However I think his main concern was reading sockets.
We have our own high performance socket handling stuff,
using epoll (linux) kqueue (BSD, OSX), and io completion
ports (Windows). On linux we may even go down to
direct kernel calls with the new io stuff in 2.6 kernels.

The interest in SDL is for gaining access to multi-media
devices – video, audio, mouse, joystick – with a portable
interface.

Hmm… looks like the approach of fastevents. I am not sure.
However I think it’s not possible to block indefinitely wait
user input (event)

My code didn’t. The trick is to make reading events
and doing graphics mutually exclusive, in case the underlying
system isn’t thread safe. So there is a mutex to establish
the exclusions. The only problem then is that SDL_WaitEvents
may be blocked, so we simply call SDL_PostEvent with a dummy event
which unblocks it.

This is much better than polling. Or rather,
it would be if it worked :slight_smile: It works on Linux,
because Xlib doesn’t care which thread reads
the event queue: AFAIK SDL actually runs its event
loop in a thread? On Windows it doesn’t, because
Windows has an event queue for every thread.

In order to implement timers you are possibly have also
your own queue on top of the SDL queue.

We don’t use SDL for timers. Felix is a general purpose
programming language. We provide timers and sockets using
the best possible underlying OS techniques, then hide all
that in an abstraction layer. SDL is for multi-media:
we don’t want to force people writing web servers
(for example) to use SDL. Thus, we don’t use SDL timers,
threads, or sockets, since we have our own, with interfaces
specifically designed to integrate with the synchronous
fthread things.

I think that the
problem is you have to wake up once in a while in order to
give the chance to the lower levels to fill up their queues…

Yes indeed. That’s why a thread is good, because it reads the
events without the mainline needed to poll. The thread automatically
gets control when the mainline blocks, and possibly even while
it is running – unless it is locked out by the mutex. However
unless you’re saturating the CPU, drawing graphics has to leave
some time free, and, at a framerate of 20FPS+, it is unlikely the
user can fill up the OS event queue in one frame … I certainly
can’t type 20 chars per second, and I’m sure the OS can queue
more than 1 keyboard event at a time :slight_smile:

Anyhow, I have done the busy/wait loop workaround, it works
on Linux, but an unrelated link problem is stopping me testing
it on Windows.On Fri, 2006-02-10 at 17:34 +0200, Vassilis Virvilis wrote:


John Skaller
Felix, successor to C++: http://felix.sf.net

Windows allows reading another threads events,
so if a record is kept of the thread which creates
the window, then any thread can read the events.
Events can also be sent across thread boundaries.

I have no idea if the API is sustained using DirectX though.

Is there any interest in fixing SDL events so they work
in separate threads on Win32? Anyone see any problems
doing it?

Sure, sounds fine to me. This would allow implementation of
SDL_INIT_EVENTTHREAD on Windows.

Remember that the code must run on Windows 98/ME.

-Sam Lantinga, Senior Software Engineer, Blizzard Entertainment