SDL events and threads on Win32 |
skaller
Guest
|
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 <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net |
|||||||||||
|
SDL events and threads on Win32 |
Alex Barry
Guest
|
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 <skaller at users.sourceforge.net> wrote:
-- Smith: "...Why, Mr Anderson, Why - Do - You - Persist?" Neo: "Because I choose to." -Matrix Revolutions -------------- next part -------------- An HTML attachment was scrubbed... URL: http://lists.libsdl.org/pipermail/sdl-libsdl.org/attachments/20060209/61bb1651/attachment.htm |
|||||||||||||
|
SDL events and threads on Win32 |
skaller
Guest
|
On Thu, 2006-02-09 at 07:54 -0500, Alex Barry wrote:
Because there is no main loop :) -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net |
|||||||||||||
|
SDL events and threads on Win32 |
SDL events and threads on Win32 |
skaller
Guest
|
On Thu, 2006-02-09 at 17:59 +0200, Vassilis Virvilis wrote:
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: =========================================================== /* 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 <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net |
|||||||||||||||||
|
SDL events and threads on Win32 |
Vassilis Virvilis
Guest
|
skaller wrote:
[snip most of a nice informative article] I haven't heard about felix. Looks nice...
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
Yes there is! You are just hiding it very well :-) [snip]
That's the right thing to do.
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
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...
Noble goal for sure and Felix. .bill |
|||||||||||||||||||||||||||||||||
|
SDL events and threads on Win32 |
skaller
Guest
|
On Fri, 2006-02-10 at 17:34 +0200, Vassilis Virvilis wrote:
:)
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'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.
I think so too .. 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 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 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.
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 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.
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.
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 :) 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. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net |
|||||||||||||||||||||||||||||||||||
|