Contents

Class Monitor


Inheritance:

Object  »
  Monitor

A monitor provides process synchronization that is more high level than the one provided by a Semaphore. Similar to the classical definition of a Monitor it has the following properties:

  1. At any time, only one process can execute code inside a critical section of a monitor.
  2. A monitor is reentrant, which means that the active process in a monitor never gets blocked when it enters a (nested) critical section of the same monitor.
  3. Inside a critical section, a process can wait for an event that may be coupled to a certain condition. If the condition is not fulfilled, the process leaves the monitor temporarily (in order to let other processes enter) and waits until another process signals the event. Then, the original process checks the condition again (this is often necessary because the state of the monitor could have changed in the meantime) and continues if it is fulfilled.
  4. The monitor is fair, which means that the process that is waiting on a signaled condition the longest gets activated first.
  5. The monitor allows you to define timeouts after which a process gets activated automatically.

Instance variables

Instance methods

Examples of Use

The first example demonstrates the transmission of data from a producer to a consumer.

 | monitor producer consumer isEmpty variable oc finished |
monitor := Monitor new.
isEmpty := true.
variable := nil.
finished := Semaphore new.
oc := OrderedCollection new.

producer := [
  1 to: 10 do:
  [:i |
      monitor critical:
         [isEmpty ifFalse: [monitor wait].
          variable := 2*i.
          isEmpty := false.
          monitor signal
         ]
  ].
 finished signal.
].

consumer := [
   1 to: 10 do:
   [:j |
        monitor critical:
         [isEmpty ifTrue: [monitor wait].
          oc add: variable.
          isEmpty := true.
          monitor signal.
         ]
   ].
  finished signal.
].

producer forkAt: Processor userBackgroundPriority.
consumer forkAt: Processor userBackgroundPriority.

finished wait;
         wait.
oc inspect

The second example is a reworked version of the first example. The reworked example demonstrates the use of conditional waits.

  | monitor producer consumer isEmpty variable oc finished |
monitor := Monitor new.
isEmpty := true.
variable := nil.
finished := Semaphore new.
oc := OrderedCollection new.

producer := [
  1 to: 10 do:
  [:i |
      monitor critical:
         [monitor waitUntil: [isEmpty].
          variable := 2*i.
          isEmpty := false.
          monitor signal.
         ]    
   ].
  finished signal.
].

consumer := [
   1 to: 10 do:
   [:j |
        monitor critical:
         [monitor waitWhile: [isEmpty].
          oc add: variable.
          isEmpty := true.
          monitor signal.
         ]
   ].
  finished signal.
].


producer forkAt: Processor userBackgroundPriority.
consumer forkAt: Processor userBackgroundPriority.

finished wait;
         wait.
oc inspect

In the first and second example, the forking process uses a Semaphore to wait for the forked processes to notify their termination. In the third example, this semaphore is removed and the monitor is used to implement the waiting for process completion. To accomplish this, the example uses an additional counter variable, the named event #finish and a conditional wait statement:

  | monitor producer consumer isEmpty variable oc users |
monitor := Monitor new.
isEmpty := true.
variable := nil.

oc := OrderedCollection new.
users := 2.

producer := [
  1 to: 10 do: [:i |
      monitor critical:
         [monitor waitUntil: [isEmpty].
          variable := 2*i.
          isEmpty := false.
          monitor signal
   ]     ].
 monitor critical:
   [users := users - 1.
    monitor signal: #finish].
].

consumer := [
   1 to: 10 do: [:j |
        monitor critical:
         [monitor waitWhile: [isEmpty].
          oc add: variable.
          isEmpty := true.
          monitor signal]
   ].
 monitor critical:
   [users := users - 1.
    monitor signal: #finish].

].

producer forkAt: Processor userBackgroundPriority.
consumer forkAt: Processor userBackgroundPriority.

monitor critical:
  [monitor waitUntil: [users = 0]
           for: #finish.
  ].
oc inspect

It should be noted, that the same example can be programmed with critical sections that enclose all the activities of the processes:

 | monitor producer consumer isEmpty variable oc users |
monitor := Monitor new.
isEmpty := true.
variable := nil.

oc := OrderedCollection new.
users := 2.

producer := [
  monitor critical:
   [1 to: 10 do:
      [:i |
          monitor waitUntil: [isEmpty].
          variable := 2*i.
          isEmpty := false.
          monitor signal
      ].
    users := users - 1.
    monitor signal: #finish
   ].
].

consumer := [
   monitor critical:
    [1 to: 10 do:
       [:j |
          monitor waitWhile: [isEmpty].
          oc add: variable.
          isEmpty := true.
          monitor signal.
       ].
     users := users - 1.
     monitor signal: #finish.
    ].
].

producer forkAt: Processor userBackgroundPriority.
consumer forkAt: Processor userBackgroundPriority.

monitor critical:
  [monitor waitUntil: [users = 0]
           for: #finish.
  ].
oc inspect

Contents