views:

203

answers:

3

I'm getting in trouble. I'm trying to emulate the call Application.Run using Application.DoEvents... this sounds bad, and then I accept also alternative solutions to my question...

I have to handle a message pump like Application.Run does, but I need to execute code before and after the message handling. Here is the main significant snippet of code.

// Create barrier (multiple kernels synchronization)
sKernelBarrier = new KernelBarrier(sKernels.Count);

foreach (RenderKernel k in sKernels) {
    // Create rendering contexts (one for each kernel)
    k.CreateRenderContext();
    // Start render kernel kernels
    k.mThread = new Thread(RenderKernelMain);
    k.mThread.Start(k);
}

while (sKernelBarrier.KernelCount > 0) {
    // Wait untill all kernel loops has finished
    sKernelBarrier.WaitKernelBarrier();
    // Do application events
    Application.DoEvents();
    // Execute shared context services
    foreach (RenderKernelContextService s in sContextServices)
        s.Execute(sSharedContext);

    // Next kernel render loop
    sKernelBarrier.ReleaseKernelBarrier();
}

This snippet of code is execute by the Main routine. Pratically I have a list of Kernel classes, which runs in separate threads, these threads handle a Form for rendering in OpenGL. I need to synchronize all the Kernel threads using a barrier, and this work perfectly. Of course, I need to handle Form messages in the main thread (Main routine), for every Form created, and indeed I call Application.DoEvents() to do the job.

Now I have to modify the snippet above to have a common Form (simple dialog box) without consuming the 100% of CPU calling Application.DoEvents(), as Application.Run does.

The goal should be to have the snippet above handle messages when arrives, and issue a rendering (releasing the barrier) only when necessary, without trying to get the maximum FPS; there should be the possibility to switch to a strict loop to render as much as possible.

How could it be possible?

Note: the snippet above must be executed in the Main routine, since the OpenGL context is created on the main thread. Moving the snippet in a separated thread and calling Application.Run is quite unstable and buggy...

+1  A: 

Don't do it - this stuff is pretty complex and I'm pretty sure you get nothing but trouble for implementing it yourself.

Can't you use Application.AddMessageFilter() to implement what you need?

Lucero
I can handle a specific message to issue rendering, but I wonder how to switch in a loop which render as much as possible. Sending repeatly a message from a separated thread? (And this imply the use of Application.Run instead the snippet, which make harder the use of the barrier)
Luca
Just post (not send!) a message that you handle, and at the end of the handler re-post the same message again. This creates kind of an infinite loop while keeping everything else running.
Lucero
A: 

If you're going to build a message loop like this, you should PInvoke the actual Win32 message handling functions (which is all that Application.Run is doing behind the scenes -- it has an internal class called UnSafeNativeMethods that maps a bunch of them).

If you don't need to keep processing between message calls -- in other words, if it's safe for your thread to sleep when it's not actively handling a message -- then bind WaitMessage from User32.dll and put it in a loop like:

while (WaitMessage())
{
  Application.DoEvents();
}

If you need more help let me know. I'm in the middle of a VS reinstall right now or I'd post a sample application showing how to do the binding and PInvoke.

Dan Story
I'd like to avoid PInvoke (or at least, having specific PInvoke for WIN32, X11 and OSX)... sleep doesn't sound good, isn't it?
Luca
Ah. If you're trying to write something cross-platform, then Application.DoEvents() in a loop is as good as you're going to get, unfortunately. The message pump design between Win32, X11 and OSX is very different, and while the Mono framework does a good job of hiding it from you and making it all look like the Win32 architecture, it doesn't expose any fine-grain control to work with its abstraction.
Dan Story
+1  A: 

There isn't anything fundamentally wrong with calling Application.DoEvents() in a loop. That's what Form.ShowDialog() does. It takes counter-measures to ensure the user cannot get into trouble: it disables all windows other than the dialog so the user cannot exit the application or start the dialog again.

You'll need to create your own, set a global flag that indicates that your main window was closed so you can immediately exit the loop without calling any more code when the rug is pulled out from under you.

You'll need to yield the processor to avoid 100% CPU load. The easiest way to do that is by calling Thread.Sleep(1). Check my answer in this thread for an example.

Hans Passant
Egg of Columbus. Thank you.
Luca
Nitpicking and - stupid: There's a lot of discussion about `Thread.Sleep(0)` vs `Thread.Sleep(1)` going down..
Benjamin Podszun