Previous
Contents
Next

Advanced Programming Techniques for MVC

Process Coordination


The most elementary form of process coordination uses Semaphores. A semaphore is a structure that administrates permissions to continue the execution of a process. It can

  | sema block1 block2 |

 sema := Semaphore new.
 block1 := 
   ["sequence of statments"
    sema wait.
    "sequence of statments"].
 block2 := 
   ["sequence of statments"
    sema signal.
    "sequence of statments"].
 block1 forkAt: Processor userBackgroundPriority.
 block2 forkAt: Processor userBackgroundPriority.

This example creates two processes that can run independently until the process that evaluates the statements in block1 asks for a permission to continue. This is done with the statement sema wait. The process that evaluates the statements in block2 grants that permission when it evaluates the statement sema signal.

Synchronization involves exactly two processes:

The process that ask for a permission will be suspended if a permission is not immediately available. The process that grants a permission can always immediately continue execution.

This example may leave you with the impression that Semaphores are a complication that does not solve a problem. To show how semaphores are used to solve a problem, we need a more detailed example. Here it is:

 | block1 block2 
  permissionToWrite permissionToRead
  aboutToTerminate 
  sharedVariable oc |

  permissionToWrite := Semaphore new.
  permissionToRead := Semaphore new.
  aboutToTerminate := Semaphore new.
  
  permissionToWrite signal.
  oc := OrderedCollection new.
  
  block1 :=
    [ 1 to: 100 do:
         [:idx |
            permissionToWrite wait.
            sharedVariable := idx * idx.
            permissionToRead signal
          ].
      aboutToTerminate signal.
    ].

 block2 :=
  [ 1 to: 100 do:
       [:idx2 |
          permissionToRead wait.
          oc add: sharedVariable.
          permissionToWrite signal
       ].
    aboutToTerminate signal.
  ].

 block1 forkAt: Processor userBackgroundPriority.
 block2 forkAt: Processor userBackgroundPriority.

  aboutToTerminate wait;
                   wait.
  oc inspect.

Explanations

In this example we have two processes that use a single shared variable to exchange a sequence of values.

Cooperation is implemented by exchange of permissions. The writing process grants a read permission after it has assigned a value to the shared variable. The reading process grants a write permission after it has fetched the current value of the shared variable. Both processes ask for permission to access the shared variable. It can therefore not happen

For the termination of both processes it is crucial that the repetition statements in both processes are evaluated the same number of times.

There are two different to ensure that the number of repetitions is the same in both processes.:

Independent evaluation of the same stop condition in two processes:

 | block1 block2 
  permissionToWrite permissionToRead
  aboutToTerminate 
  sharedVariable oc |

  permissionToWrite := Semaphore new.
  permissionToRead := Semaphore new.
  aboutToTerminate := Semaphore new.
  
  permissionToWrite signal.
  oc := OrderedCollection new.
  
  block1 :=
    [  | idx square |
      idx := 1.
      [square := idx * idx.
       permissionToWrite wait.
       sharedVariable := square.
       permissionToRead signal.
       square < 100000
      ]
        whileTrue:
           [idx := idx + 1].
     aboutToTerminate signal.
    ].

  block2 :=
    [[permissionToRead wait.
      oc add: sharedVariable.
      permissionToWrite signal.
      oc last < 100000
     ]
       whileTrue.
     aboutToTerminate signal
    ].

  block1 forkAt: Processor userBackgroundPriority.
  block2 forkAt: Processor userBackgroundPriority.

  aboutToTerminate wait;
                   wait.
  oc inspect.

Evaluation of the stop condition in one process and communication of the result to the other process. This solution requires an additonal shared variable. Note also that the process block2 cannot reliably use the value of the shared variable continue after it has given a write permission for that variable. The process has to copy the value for later use.

 | block1 block2 
  permissionToWrite permissionToRead
  aboutToTerminate 
  sharedVariable continue oc |

  permissionToWrite := Semaphore new.
  permissionToRead := Semaphore new.
  aboutToTerminate := Semaphore new.
  
  permissionToWrite signal.
  oc := OrderedCollection new.
  
  block1 :=
    [  | idx square |
      idx := 1.
      [square := idx * idx.
       permissionToWrite wait.
       sharedVariable := square.
       continue := square < 100000.
       permissionToRead signal.
       continue
      ]
        whileTrue:
           [idx := idx + 1].
     aboutToTerminate signal.
    ].

  block2 :=
    [[ | mayContinue |
      permissionToRead wait.
      oc add: sharedVariable.
      mayContinue := continue.
      permissionToWrite signal.
      mayContinue
     ]
       whileTrue.
     aboutToTerminate signal
    ].

  block1 forkAt: Processor userBackgroundPriority.
  block2 forkAt: Processor userBackgroundPriority.

  aboutToTerminate wait;
                   wait.
  oc inspect.

Previous
Contents
Next