Folks - I just finished a prototype of the Win32 event VM to illustrate some of the practical impact. It makes a really nice demo: 1) Download the VM from http://squeakvm.org/win32/experimental/Squeak-3.11-EventVM.zip 2) Fire up an image with this VM (I used a trunk image) and execute AbstractSound stereoBachFugue play. 3) Observe the sound behavior you get when you: * drag the window around * go to the system menu * click the window close button In all of the above situations the sound play will be interrupted, replaying the current buffer over and over. 4) Execute the following: (ProcessorScheduler classPool at: #BackgroundProcess) terminate. You should see a printf that tells you "entering windows main loop" which indicates that you're now running under the new regime. 5) Observe the sound behavior you get when you: * drag the window around * go to the system menu * click the window close button In all of these situations the sound will now keep playing. There can be a temporary hickup due to lack of buffering but other than that everything continues smoothly. For those interested I'm attaching the relevant changes (only for playing around; these changes are NOT ready to be integrated). The Windows main loop has been changed to read like here: /* prepare active process and do first time interpret() for old images */ prepareActiveProcess(); /* do not allow recursive interpret() */ allowInterpret = 0; /* interpret() will be left after nuking background process */ interpret(); /* now do a classic Windows main loop */ printf("Entering Windows Main Loop\n"); SetTimer(stWindow, 1, 50, NULL); /* make sure we interpret() every 50ms */ allowInterpret = 1; /* okay to interpret() now */ while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } and then you call interpret() whenever you feel like it. I'm doing that from MainWndProc in response to Windows messages: /* A bit of extra paranoia: I'm not sure if calling interpret() through checkForInterrupt() recursion into ioProcessEvents() is safe (probably not). */ if(!inProcessEvents && allowInterpret) interpret(); Of course, this isn't finished yet by any means. There are some odd effects when a recursion does happen, and somewhere I'm loosing events. But I think it does illustrate the point that I'm trying to make. BTW, does anyone know if wxSqueak is still active? I think the behavior of this VM would be a perfect fit for their needs. Cheers, - Andreas EventVM.1.cs (10K) Download Attachment |
Andreas, I think this recursion situation will lead to undesirable concurrency safety issues (you are interrupting squeak processes at unpredictable and likely unsafe points). I'm sure you already know this. I mentioned having two calls, one for interpret() and another for handling events (let's call it sendMessage()). The latter (sendMessage()) might be implemented by having a general call that, rather than scheduling and interpreting all active squeak processes, just dispatches and executes one specific message uninterruptably and returns without allowing other processes to be scheduled. You would somehow need to interrupt interpret() if it is actively running squeak processes for the duration of sendMessage() and you would probably want to disable GC during this call. It might also work to simply make sendMessage() wait until interpret() completes (but then I think you might be more at the mercy of OS buffer overflow handling behavior rather than be able to do something more intelligent in overflow situations inside your message enqueueing logic (i.e. giving priority to certain kinds of events while more eagerly dropping others)).
You would call sendMessage() in response to Windows messages (it would do nothing but safely and quickly enqueue messages for interpret()). You could have a third call, waitUntilRunnable() and then you would have a loop in an OS level thread that repeatedly calls waitUntilRunnable() followed by interpret(). The Windows messages would be calling sendMessage() to unblock waitUntilRunnable() and queue up work for interpret().
- Stephen On Sat, Nov 14, 2009 at 1:02 AM, Andreas Raab <[hidden email]> wrote:
|
2009/11/14 Stephen Pair <[hidden email]>: > > Andreas, > I think this recursion situation will lead to undesirable concurrency safety issues (you are interrupting squeak processes at unpredictable and likely unsafe points). I'm sure you already know this. I mentioned having two calls, one for interpret() and another for handling events (let's call it sendMessage()). The latter (sendMessage()) might be implemented by having a general call that, rather than scheduling and interpreting all active squeak processes, just dispatches and executes one specific message uninterruptably and returns without allowing other processes to be scheduled. You would somehow need to interrupt interpret() if it is actively running squeak processes for the duration of sendMessage() and you would probably want to disable GC during this call. It might also work to simply make sendMessage() wait until interpret() completes (but then I think you might be more at the mercy of OS buffer overflow handling behavior rather than be able to do something more intelligent in overflow situations inside your message enqueueing logic (i.e. giving priority to certain kinds of events while more eagerly dropping others)). > You would call sendMessage() in response to Windows messages (it would do nothing but safely and quickly enqueue messages for interpret()). You could have a third call, waitUntilRunnable() and then you would have a loop in an OS level thread that repeatedly calls waitUntilRunnable() followed by interpret(). The Windows messages would be calling sendMessage() to unblock waitUntilRunnable() and queue up work for interpret(). i'm not sure i get everything.. but given the complexity and constraints (non-interruptable, no GC).. is a bit high. I think better would be to make sure there is no recursive call to interpret(), rather than deal with consequences. I'm already proposed that. > - Stephen > On Sat, Nov 14, 2009 at 1:02 AM, Andreas Raab <[hidden email]> wrote: >> >> >> Folks - >> >> I just finished a prototype of the Win32 event VM to illustrate some of the practical impact. It makes a really nice demo: >> >> 1) Download the VM from >> >> http://squeakvm.org/win32/experimental/Squeak-3.11-EventVM.zip >> >> 2) Fire up an image with this VM (I used a trunk image) and execute >> >> AbstractSound stereoBachFugue play. >> >> 3) Observe the sound behavior you get when you: >> * drag the window around >> * go to the system menu >> * click the window close button >> In all of the above situations the sound play will be interrupted, replaying the current buffer over and over. >> >> 4) Execute the following: >> >> (ProcessorScheduler classPool at: #BackgroundProcess) terminate. >> >> You should see a printf that tells you "entering windows main loop" which indicates that you're now running under the new regime. >> >> 5) Observe the sound behavior you get when you: >> * drag the window around >> * go to the system menu >> * click the window close button >> In all of these situations the sound will now keep playing. There can be a temporary hickup due to lack of buffering but other than that everything continues smoothly. >> >> For those interested I'm attaching the relevant changes (only for playing around; these changes are NOT ready to be integrated). The Windows main loop has been changed to read like here: >> >> /* prepare active process and do first time interpret() for old images */ >> prepareActiveProcess(); >> /* do not allow recursive interpret() */ >> allowInterpret = 0; >> /* interpret() will be left after nuking background process */ >> interpret(); >> >> /* now do a classic Windows main loop */ >> printf("Entering Windows Main Loop\n"); >> SetTimer(stWindow, 1, 50, NULL); /* make sure we interpret() every 50ms */ >> allowInterpret = 1; /* okay to interpret() now */ >> while (GetMessage(&msg, NULL, 0, 0) > 0) { >> TranslateMessage(&msg); >> DispatchMessage(&msg); >> } >> >> and then you call interpret() whenever you feel like it. I'm doing that from MainWndProc in response to Windows messages: >> >> /* A bit of extra paranoia: I'm not sure if calling interpret() >> through checkForInterrupt() recursion into ioProcessEvents() >> is safe (probably not). */ >> if(!inProcessEvents && allowInterpret) interpret(); >> >> Of course, this isn't finished yet by any means. There are some odd effects when a recursion does happen, and somewhere I'm loosing events. But I think it does illustrate the point that I'm trying to make. >> >> BTW, does anyone know if wxSqueak is still active? I think the behavior of this VM would be a perfect fit for their needs. >> >> Cheers, >> - Andreas >> >> > > > -- Best regards, Igor Stasenko AKA sig. |
In reply to this post by Stephen Pair
Stephen Pair wrote: > You would call sendMessage() in response to Windows messages (it would > do nothing but safely and quickly enqueue messages for interpret()). > You could have a third call, waitUntilRunnable() and then you would > have a loop in an OS level thread that repeatedly calls > waitUntilRunnable() followed by interpret(). The Windows messages would > be calling sendMessage() to unblock waitUntilRunnable() and queue up > work for interpret(). Interestingly, we have all that just under different names. The sendMessage() call is implemented in C (via recordMouseEvent, recordKeyboardEvent etc) in such that its purpose is to "safely and quickly enqueues the nessage for interpret" ;-) The function waitUntilRunnable() is implemented implicitly via having interpret call from the MainWndProc (which isn't called unless there's an event) but in an earlier version I wrote a more explicit loop like: while(1) { /* here goes waitUntilRunnable */ ioRelinquishProcessorForMicroseconds(1000); ioProcessEvents(); /* and finally interpret() */ interpret(); } The issue with recursion is not as difficult as it might seem. The important thing (as you said) is knowing when a callback is "safe". And that's actually quite easy to determine - callbacks are safe if and only if you're in a primitive because only then all necessary interpreter state is sufficiently externalized. The callbackEnter implementation already makes use of that and I just haven't adapted all the necessary code. (I did prevent callbacks via ioProcessEvents since this is called from checkForInterrupts in strange places but that doesn't seem to be enough). Cheers, - Andreas |
Free forum by Nabble | Edit this page |