general morphic concurrency question

Andreas Raab andreas.raab at gmx.de
Tue Dec 14 03:28:34 UTC 2004


You need to synchronize your data to the world's interaction cycle.
A fairly simple way of doing this is the following:

plotMorph := PlotMorph new.
plotMorph extent: 400 at 400.
plotMorph openInWorld.
[
    0 to: 1000 do: [ :index |
        WorldState addDeferredUIMessage:[
            plotMorph series: #a addPoint: index@(index cos)
        ].
        (Delay forMilliseconds: 10) wait.
    ]
] fork.


The block passed to WorldState>>addDeferredUIMessage: will be evaluate in 
sync with the rest of the system.

A more complex (but much better structured) way is to use a SharedQueue for 
the interaction, e.g., you'd do something like:

PlotMorph subclass: #MyPlotMorph
    instanceVariableNames: 'queue'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'My-Plot-Morph'

MyPlotMorph>>initialize
    "Initialize the receiver"
    super initialize.
    queue := SharedQueue new.

MyPlotMorph>>queue
    "answer the receiver's point queue"
    ^queue

MyPlotMorph>>wantsSteps
    "Answer whether I want to see steps"
    ^true

MyPlotMorph>>stepTime
    "Answer the refresh interval for the receiver. Here,
    this means how often do we want to redraw any incoming points?"
    ^20 "50/sec"

MyPlotMorph>>step
    "Read all the points so far"
    | point |
    [point := queue nextOrNil.
    point isNil] whileFalse:[
        self series: #a addPoint:point
    ].

And then you'd use it like this:

plotMorph := MyPlotMorph new.
plotMorph extent: 400 at 400.
plotMorph openInWorld.
queue := plotMorph queue.
[
    0 to: 1000 do: [ :index |
        queue nextPut: index@(index cos).
        (Delay forMilliseconds: 10) wait.
    ]
] fork.

The latter way gives you much better control about how precisely you'd want 
the system to behave (in terms of trading off speed for the updating of the 
plot vs. speed of reading points) and allows for pretty easy integration of 
other data sources (just add another queue). Etc.

Cheers,
  - Andreas

----- Original Message ----- 
From: <emerson at cs.pdx.edu>
To: <squeak-dev at lists.squeakfoundation.org>
Sent: Monday, December 13, 2004 4:53 PM
Subject: general morphic concurrency question


> Howdy!
>
> I've got a general question about concurrency in squeak.  It will be 
> easier for
> me to explain the question if I can explain how I arrived at it - so 
> please
> bear with me!
>
> I've been trying to do some graphing of streaming data using PlotMorph, to 
> add a
> series of points to a graph as the point data is coming in.  Here's a 
> simplified
> snippet, where the forked block represents the stream drawing to the 
> PlotMorph
> every so often:
>
> plotMorph := PlotMorph new.
> plotMorph openInWorld.
>
> [
> 0 to: 1000 do: [ :index |
> plotMorph series: #a addPoint: index@(index cos).
> (Delay forMilliseconds: 10) wait.
> ]
> ] fork.
>
> At some point during execution, an instance variable (a Form) of the 
> plotMorph
> will be set to nil and a message will be sent to it.  Bad news!  I've 
> tracked
> the problem to two threads:
>
> (1) One thread is the WorldState, trying to update all damaged Morphs. 
> When it
> gets to plotMorph, it calls PlotMorph>>form, which starts by lazily
> initializing  the Form and then doing some housekeeping...
>
> (2) Meanwhile (sort of), my thread in the code above adds another point to
> plotMorph, and signals the PlotMorph>>changed method, which sets form to 
> nil.
>
> By the time thread 1 has exits the PlotMorph>>form method, form had been 
> reset
> to nil by thread 2.  Oops!
>
>
> A solution to the problem would be to disallow the adding of points (or 
> more
> generally, calls to Morph>>changed) until the WorldState is finished 
> drawing.
> This seems like a more general problem, one that has probably been solved
> elsewhere.  Is it, and has it?  Could someone fill me in?
>
> Thanks,
>
> Emerson
> 




More information about the Squeak-dev mailing list