[squeak-dev] Future examples (Re: Inbox: #future keyword for
asynchronous message invocation)
Igor Stasenko
siguctua at gmail.com
Thu Dec 17 22:16:03 UTC 2009
2009/12/17 Andreas Raab <andreas.raab at gmx.de>:
> Josh Gargus wrote:
>>
>> Comments from others?
>
> I think people are probably not completely clear what kinds of
> simplifications future messages allow. I just found a great example to
> illustrate the difference: Dynamic scroll bar delays.
>
> The current situation
> =====================
> Currently, if you look at Scrollbar you'll find that continuous scrolling is
> handled in a fairly complex way via:
>
> doScrollDown
> "Scroll automatically while mouse is down"
> (self waitForDelay1: 200 delay2: 40) ifFalse: [^ self].
> self setValue: (value + scrollDelta + 0.000001 min: 1.0)
>
> and then
>
> waitForDelay1: delay1 delay2: delay2
> "Return true if an appropriate delay has passed since the last scroll
> operation.
> The delay decreases exponentially from delay1 to delay2."
>
> | now scrollDelay |
> timeOfLastScroll isNil ifTrue: [self resetTimer]. "Only needed
> for old instances"
> now := Time millisecondClockValue.
> (scrollDelay := currentScrollDelay) isNil
> ifTrue: [scrollDelay := delay1 "initial delay"].
> currentScrollDelay := scrollDelay * 9 // 10 max: delay2.
> "decrease the delay"
> timeOfLastScroll := now.
> ^true
>
> #doScrollDown itself is called repeatedly via #step (see also
> #scrollDownInit, #step, #wantsSteps, #stepTime etc). And of course there are
> variants on this theme for scrolling up, down, page up and down and more.
>
> Using timed future: messages
> ============================
> Now let's look at the implementation when using a timed future instead:
>
> ScrollBar>>scrollDownInit
> 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.
>
> Using non-timed future messages
> ===============================
> And if you don't like that pattern, there's an interesting alternative using
> non-timed futures and rather an explicitly forked block:
>
> ScrollBar>>scrollDownInit
> downButton borderInset.
> keepScrolling := true.
> [self doScrollDown] fork.
>
> ScrollBar>>doScrollDown
> "keep scrolling as long as the mouse is down"
> delay := 200.
> [keepScrolling] whileTrue:[
> self future setValue: (value + scrollDelta + 0.000001 min: 1.0).
> (Delay forMilliseconds: delay) wait.
> delay := delay * 9 // 10 max: 40. "delay2"
> ].
>
> (the example has a small bug which I'll ignore for educational reasons - if
> you can spot it you're good ;-)
IMO, the bug is setting a value in future, but not immediately.
> 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.
>
Hmm.. can't see how futures helping to deal with concurrency. Unless
there some details which i don't see.
A semantics of 'future' is guarantee that message will be sent
eventually in future,
but there is no need to guarantee that this message order will be
preserved e.g.:
self future foo.
self future bar.
you having same chances to receive #foo then #bar, as well as #bar then #foo.
And if some other code poking with your data and interrupted to handle
future message send,
you still might need to use a synchronization, if both accessing same state.
> Cheers,
> - Andreas
--
Best regards,
Igor Stasenko AKA sig.
More information about the Squeak-dev
mailing list
|