- live browsing of Self object memory -

lobby traitsmonitor

CopyDowns: vector

CreatorPath: traits monitor

Module: monitor

parent* = traits clonable
enter =
( | thisProcess |
    thisProcess: process this.
    "Note: doing the following comparison before entering the monitor
     is safe since no other process than this one could assign to 
     'holdingProcess' in a way that makes the condition true."
    holdingProcess != thisProcess ifTrue: [
        monitorSemaphore wait.
        assert: [0 = holdCount].
        holdingProcess: thisProcess.
    ].
    holdCount: 1 + holdCount.
    printDebug: 'entered'.
    self)
This is a low-level operation. It is preferable to use 'inMonitorDo:'. If this low-level operation is used, the caller must ensure that enter/exit pair up correctly.
exit =
( 
    assertHoldsMonitor.
    holdCount: holdCount - 1.
    0 = holdCount ifTrue: [
        holdingProcess: nil.  "Clear before signalling semaphore."
        semaphoreToSignal signal.
    ].
    printDebug: 'exited'.
    self)
This is a low-level operation. It is preferable to use 'inMonitorDo:'. If this low-level operation is used, the caller must ensure that enter/exit pair up correctly.
heldByMe = ( holdingProcess = process this)Return true iff the current process holds this monitor.
inMonitorDo: =
( 
    enter.
    blk onReturn: [exit])
Execute the blk while holding the monitor. Return result of blk.
isHeld = ( holdingProcess isNotNil)
printString =
( 
    isHeld ifTrue: 'monitor(held)'
            False: 'monitor(free)')
waitCondition =
( | hc <- 0. hp |
    printDebug: 'initiating waitCondition'.
    assertHoldsMonitor.
    hc: holdCount.
    hp: holdingProcess.
    holdCount: 0.        "Release monitor."
    holdingProcess: nil.
    semaphoreToSignal signalAndThenWaitOn: conditionSemaphore.
    holdCount: hc.       "Reestablish owership."
    holdingProcess: hp.
    printDebug: 'finishing waitCondition'.
    self)
Call only when holding monitor. Will cause the process to wait outside the monitor for a signal on the 'conditionSemaphore'.
waitFor: =
( 
    conditionBlk whileFalse: [waitCondition].
    self)
Must already hold monitor when calling this method. Will release monitor temporarirly and wait for external event that makes 'conditionBlk' evalute to true. More specifically, will wait for a 'signalCondition' to be executed by other process; when signal arrives, will evaluate 'conditionBlk' and continue if it has become true; else will wait for another signal. So a process that has changed some state that may have made a condition become true, should indicate this fact my invoking 'signalCondition'. It is guaranteed that whenever 'conditionBlk' is evaluated, the process doing so holds the monitor.
semaphoreToSignal =
( 
    "Return the semaphore to signal. Give preference to processes waiting
     on conditionSemaphore to avoid starvation.
     Question: is it safe to first test which semaphore is empty and then
     later signal it without wrapping the test-signal sequence in a critical
     region? In other words, could we find 'conditionSemaphore' empty when 
     testing and then proceed to signal 'monitorSemaphore', while in the 
     meantime another process arrives at 'conditionSemaphore'?
     No, this cannot happen. For the other process to arrive at 
     'conditionSemaphore' it would have to transition through the monitor
     (which we currently hold)."
    conditionSemaphore isEmpty ifTrue: [monitorSemaphore]
                                False: [conditionSemaphore])

copying

copy = ( resend.copy init)
init =
( 
    monitorSemaphore:   semaphore copyBinary.
    conditionSemaphore: semaphore copyBinary wait.
    holdCount:          0.
    holdingProcess:     nil.
    checkInvariant.
    self)

debugging

assertHoldsMonitor =
( 
    assert: [holdingProcess = process this].
    assert: [0 < holdCount].
    assert: [0 = (monitorSemaphore count + conditionSemaphore count)].
    self)
Assert that the executing process holds the monitor.
monitorTest = traits monitor monitorTest
printDebug: =
( 
    debug ifTrue: [
        "Use '_StringPrint' to avoid frequent context switches with
         regular IO (makes the output very confusing)."
        checkInvariant.
        (process this objectID, ': ', strBlk value, '\n') _StringPrint.
    ].
    self)
test = ( monitorTest copy run)Run a test of monitors.
assert: = ( debug ifTrue: [blk assert])
checkInvariant =
( 
    false && debug ifTrue: [| c <- 0. |
        c: monitorSemaphore count + conditionSemaphore count.
        assert: [1 >= c].
        assert: [isHeld = (0 = c)].
    ].
    self)
debug = false