skaller wrote:
Why not only use one event handler and poll/wait for events in a main
loop?
Because there is no main loop 
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 
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