[squeak-dev] Future examples (Re: Inbox: #future keyword for
asynchronous message invocation)
Andreas Raab
andreas.raab at gmx.de
Thu Dec 17 21:28:44 UTC 2009
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 ;-) 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.
Cheers,
- Andreas
More information about the Squeak-dev
mailing list
|