[Vm-dev] An event driven Squeak VM
Andreas Raab
andreas.raab at gmx.de
Tue Nov 10 08:00:18 UTC 2009
Folks -
I had an interesting thought today that I'd like to run by you because I
think it might just work. I have been thinking for a long time how to
make the Squeak VM be "truly event driven" that is invoke it in response
to OS or other events instead of having the VM poll. There are lots of
good reasons for this starting from not blocking when popping up an OS
context menu or standard dialog, over being able to embed the VM into
other apps (browser plugin etc), up to properly dealing with
suspend/resume. There are various problems that could be dealt with more
easily if the VM would be truly event driven.
Today it occurred to me that there might be a relatively simple way to
deal with that problem merely by having interpret() run until "there is
no more work to do" and return from interpret() when it's all said and
done. The trick is that instead of running the idle loop, the VM would
determine that it has no more work to do when there is no runnable
process, so when it finds that there is no runnable process it would
return from interpret saying "my work's done here, there is no more code
to run at this point, ask me again when an external event comes in".
The changes would be fairly straight forward: First, nuke the idle loop
and allow wakeHighestPriority to return nil when there's no runnable
process. Second, have transferTo: do a longjmp to the registered
vmExitBuf to leave interpret(). Third, have interpret register the
vmExitBuf and wake up the highest priorty process like here:
interpret
"install jmpbuf for main interpreter"
(self setjmp: vmExitBuf) == 0 ifTrue:[
self checkForInterrupts. "timers etc"
"transferTo: longjmps if arg is nil so no need to check"
self transferTo: self wakeHighestPriority.
"this is the current interpret() implementation"
self internalizeIPandSP.
self fetchNextBytecode.
[true] whileTrue: [self dispatchOn: currentBytecode in: BytecodeTable].
].
At this point we can write a client loop that effectively looks like:
/* run the interpreter */
while(!done) {
/* check for new events */
ioProcessEvents();
/* run processes resulting from the events */
interpret();
}
Now, obviously this is inefficient, we'd want to replace the
ioProcessEvents() call with something more elaborate that reacts to the
incoming OS events, takes the next scheduled delay into account, checks
for socket handles etc. But I'm sure you're getting the idea. Instead of
wasting our time in the idleProcess, we just return when there's no more
work to do and it's up to the caller to run interpret() as often or as
rarely as desired.
I also think that this scheme could be made backwards compatible by
ensuring that we never call interpret() recursively. In this case an
"old" image with the idle process would run the way it does today, and a
"new" image without the idle process would live in the shiny new event
driven world and return as needed.
What do you think? Any reasons why this wouldn't work?
Cheers,
- Andreas
More information about the Vm-dev
mailing list