[squeak-dev] Future examples (Re: Inbox: #future keyword for
asynchronous message invocation)
josh at schwa.ca
Fri Dec 18 09:01:11 UTC 2009
On Dec 17, 2009, at 2:13 PM, Colin Putney wrote:
> On 2009-12-17, at 1:28 PM, Andreas Raab wrote:
>> Using timed future: messages
>> Now let's look at the implementation when using a timed future instead:
>> downButton borderInset.
>> keepScrolling := true.
>> self doScrollDown: 200. "i.e., delay1"
>> ScrollBar>>doScrollDown: delay
>> "keep scrolling as long as the mouse is down"
>> self setValue: (value + scrollDelta + 0.000001 min: 1.0).
>> keepScrolling ifTrue:[
>> (self future: delay) doScrollDown: (delay * 9 // 10 max: 40). "delay2"
>> That's it. No #step, no #stepTime, no #wantsSteps, no #waitForDelay etc. Just a message shot into the future by a few milliseconds. We call the above pattern "recursion in time" since it sends messages recursively to itself at some later point in time.
> We want these future sends to happen in the Morphic UI process, so the Morphic implementation of #future (via #futureDo:at:args:) implements this via #addDeferredUIMessage: ?
> Is there some process that waits on a delay and then queues the deferred UI message?
Yes. You can see this in #future:do:at:args: and #future:send:at:args:
The current implementation forks a separate process for each #future: message (for unary #future messages, no fork occurs; the message is scheduled immediately). This means that the strict ordering properties that Andreas describes in another message only hold for unary #future messages. I consider this to be a bug in the current implementation, although it doesn't matter for typical uses of #future: (such as the ScrollBar example).
However, even if we were to use a single scheduler process, there is still some ambiguity about what should be the execution order of #future: messages. For example:
(self future: 2000) foo.
(self future: 1999) bar.
Which should execute first? If a high-priority process interrupts, then the second line might not run for another 4ms. Croquet ensures a deterministic order by not advancing the clock while executing queued messages in an island, and by making it illegal to send #future: messages from outside of the island (only #future messages are allowed).
Despite this ambiguity, we can still do better than the current implementation.
>> In this example we use an untimed future message to synchronize the scrolling loop with the foreground morphic process. It's not quite as elegant as the first example but illustrates one of the primary uses for future messages - lock-free interprocess communication. Future messages allow you to use concurrency in cases where it would be very hard to synchronize with locks - in the above I would have to guard all the modifications of the scrollbar's value by some lock but using the future message allows us to introduce a level of concurrency that would be difficult to achieve otherwise.
> Pretty interesting. I'm a bit ambivalent about a magic compiler change like this, but you make a pretty strong case for it's usefulness.
That's why Andreas asked me to add the FutureMaker class and the Object future/future: implementations. The same semantics are implemented without any compiler magic; the addition of FutureNode to the compiler is then merely an optimization.
More information about the Squeak-dev