View this PageEdit this PageAttachments to this PageHistory of this PageHomeRecent ChangesSearch the SwikiHelp Guide
Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007

Multi-threaded Squeak

Multithreaded Squeak with Morphic


If you're reading this it probably means you're at least somewhat familiar with the Squeak programming language. This guide assumes a bit of familiarity with the Morphic framework in Squeak.

As you're probably aware, Morphic provides an internal stepping model for handling things like animations. Each instance of a Morphic class can set a stepTime and can have #startStepping invoked. The general programming mdoel is as follows:


This method is great for small animations and for large programs where only a single view on a model is required. The approach is however not really true MVC. Since stepping is handled within each Morph the model and view are effectively the same object. This can present significant problems if a group of model objects (which may not all be part of the same view) have to be synchronized, or if you want to have multiple views on the same object.

Fortunately, Squeak provides a fairly complete threading framework which allows a simple resolution to this problem. Unfortunately, threading and display systems (like Morphic) don't often get along well. There are solutions for making the two play nicely, however.

Stepping with Threads


First allow me to address handling of the stepping problem using a multi-threaded approach. As a general rule, stepping processes are fairly long running. This is assumed for this particular tutorial as starting threads has some significant overhead associated with it. Therefore short running processes should resort to means of control other than those described here.

Creating a thread in Squeak is as simple as sending a message. Specifically, any block can be sent the #fork message causing it to execute in parallel to the calling code (and obviouslly potentially outlive the entire calling context). A simple example might appear as follows:

[ 
  Transcript show: 'Hello world'.
  (Delay forMilliseconds: 10000) wait.
  Transcript show: 'Goodbye world'.
] fork.


Open a transcript and try executing that chunk of code in a workspace. You'll notice that the code returns immediately, printing only "Hello world" on the transcript. Ten seconds later Goodbye world will finally appear. Now try the code without the surrounding block (or change fork to value). Your Squeak image will appear to lock up for 10 seconds while the Delay executes. Thus the utility of threads is demonstrated in a 5 line example.

Also, I've shown you just about everything you need to know to create a full stepping model simulation in that same example. The basic pattern we will follow in our stepping system is to execute some code, delay for a while, then execute the code again.

At this point it's probably a good idea to create a new class to contain all of your work. I recommend calling it something along the lines of SimpleStepper (which is what I'll assume for the rest of this guide). Now, ideally you'd like to be able to see if the stepper is running, and we're going to need a convenient way to stop things later, so add an instance variable called 'running' to the class you've created. Go ahead and add an #initialize method which sets running to false on class creation.

In addition, it's be good to keep a collection of objects that we're stepping around, so add another instance variabled called targets and initialize this to a Bag. At this point you should have an initalize method like the following (in fact, if you just paste this code it will give you the chance to create new instance variables when you save the method):

initialize

    running _ false.
    targets _ Bag new.


Since we don't have any way to add stepping targets yet, let's take care of that. Go ahead and add two methods, #addTarget: and #removeTarget: to your class. These should add and remove their parameters from the targets collection (obviously). This has actually introduced a subtle bug, but we'll worry about that later. Now, let's go ahead and add a method called #step. This method should do the obvious of sending #step to all of our targets. You ought to be able to write this for youself, but just in case it should look like the following:

step

    targets do: [ :target |
        target step.
    ].


If you'd like you can test this out with some of your existing stepping Morphs. Create a new instance of your SimpleStepper class, addTarget: a Morph that isn't stepping and send your SimpleStepper the #step message. Your morphs should all advance one step. Obviously this still isn't what we're looking for though, we're having to invoke step by hand. So go ahead and define a #runStepper method which invokes step for us. Again, you ought to be able to do this based on the code above, but here's an example nonetheless:

runStepper

    [ running ] whileTrue: [
        self step.
        (Delay forMilliseconds: 100) wait.
    ].


You'll note that I've included a fixed 100 millisecond delay. If you'd like you can add an instance variable which controls this delay, along with methods for fetching/changing it. You should notice that if you try invoking this method on your SimpleStepper that nothing happens. That's because we set running to false in initalize and the method just returns when running is false. Clearly not the result we're looking for! So, let's add a method which sets running to true, along with kicking off the stepper. While we're at it, let's add a #stopStepping message as well.

startStepping

    running ifFalse: [
        running _ true.
        [ self runStepper ] fork.
    ].

stopStepping

    running _ false.


Simple enough, if we're not already running start running. If we are, do nothing. To stop things we just set running to false and the stepper will return as soon as its delay finishes. This code has a number of bugs, some of which we'll fix in a moment, others of which I'll explain but leave as exercises (because they're somewhat annoying to fix :). For most situations this is a completely functional multi-threaded stepper, though.

Since robustness of code is always a good thing, let's fix some of the bugs I mentioned earlier. First of all, it's a really bad idea to add items to a colleciton that you're iterating over at the same time. Unfortunately the above example doesn't deal with this at all. There is where concept of Semaphores and Mutexes come into play. Both of these are tools for making sure that only as many threads as should be able to access an object at any given time can. The other threads will wait until the obeject becomes available to them. Squeak handles both of these objects via the Semaphore class. Let's add another instance variable to our SimpleStepper called semaphore. Update #initalize to set this to a new instance of the Semaphore class.

Writing Safe Threaded Code


Now, Semaphore can be used in multiple ways. In our case we want it to behave as a Mutex which is handled through the #critical: message. If one thread invokes #critical: with a block on a Semaphore, any other thread attempting to invoke #critical: will wait until the first block is done executing. Let's go ahead and add the following changes to our existing methods to make them take advantage of this functionality:

runStepper

    [ running ] whileTrue: [
        semaphore critical: [
            self step.
        ].
        (Delay forMilliseconds: 100) wait.
    ].
    
addTarget: aTarget

    semaphore critical: [
        targets add: aTarget
    ].
    
removeTarget: aTarget

    semaphore critical: [
        targets add: aTarget
    ].


These changes will prevent us from trying to add or remove anything from our target list while we're in the middle of a step. Since every operation on the targets list is now contained within a critical block the list will be safe from harm while it's being iterated over.

Let's also eliminate a race condition on startup using the same semaphore. Update startStepping to read as follows:

startStepping

    semaphore critical: [
        running ifTrue: [
            ^ false 
        ] ifFalse: [
            running _ true
        ].
    ].
    
    [ self runStepper ] fork.


Prior to this change, two calls to startStepping from different threads could cause two instances of the stepper to start executing. Note there there is still a bug that can result if startStepping is called within one step time of calling stopStepping. This will result in two separate steppers running, a problem which is somewhat annoying to fix but can be handled by the addition of another instance variable and a two step lock on starting and stopping the simulation. The actual implementation details of this are left as an exercise.

Now as for the subject of Morphic. Trying to access objects which are targets of our stepper from Morphic can cause problems if they're in the middle of a step. The easiest solution to this problem is to add some functionality to the Object class in Squeak. This allows anything to be added to a SimpleStepper and have locking functionality. Go ahead and add a stepper instance variable to the Object class and initalize this to nil in initalize. Also add #stepper and #stepper: methods to it. Finally, add a #lock: method which takes a parameter of a block. It should look like the following:

lock: aBlock

    stepper isNil ifTrue: [
        ^ aBlock value
    ] ifFalse: [
        ^ stepper lock: aBlock
    ].


Also, add a #lock: method to SimpleStepper which looks like:

lock: aBlock

    semaphore critical: [
        ^ aBlock value
    ].


Finally, update #addTarget: and #removeTarget: to add the stepper to each target added like so:

addTarget: aTarget

    semaphore critical: [
        aTarget stepper isNil ifFalse: [
            ^ self error: 'Objects may only have one stepper at a time'.
        ].
        aTarget stepper: self.
        targets add: aTarget.
    ].

removeTarget: aTarget

    semaphore critical: [
        aTarget stepper = self ifTrue: [
            aTarget stepper: nil.
            targets remove: aTarget.
        ].
    ].


Alright, so that was a lot of changes. Here's what just happened. First, we added the ability to lock a block of code tied to a given object against multiple threads executing at once. We tied the lock to the stepper associated with the object (if there is no stepper we just transparently let the block execute in the calling thread). Second, we made sure that only one stepper can be tied to an object at a time. Also, we made sure you can't accidentally clobber an object's stepper reference from another stepper.

Now you can access the object whenever you'd like from Morphic without worrying about the state changing mid access. Simply wrap all object accesses in a lock: block like so:

draw

    model lock: [
        self position: model position
    ].
    
    ^ super draw.


This example causes the Morph to reposition itself each time it's drawn, safely locking the model object before it accesses it.

That's it, that's all there is to creating a simple multi-threaded stepper in Squeak! Hope that was helpful :)

Questions or comments may be directed to Jon Olson jsolson@damogran.org


Links to this Page