[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