[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