RecursionLock.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Wed, 10 Jun 2015 08:43:00 +0100
branchjv
changeset 18482 68a43e2b3e78
parent 18120 e3a375d5f6a8
child 18749 d6947ad2feaf
permissions -rw-r--r--
Merge

"
 COPYRIGHT (c) 1995 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic' }"

Object subclass:#RecursionLock
	instanceVariableNames:'process sema'
	classVariableNames:''
	poolDictionaries:''
	category:'Kernel-Processes'
!

!RecursionLock class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1995 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

!

documentation
"
    like a Semaphore for mutual exclusion, but avoids the deadlock
    if a critical region is reentered by the same process again.
    I.e. allows reentering the critical region IFF the current process 
    is the one which did the original locking.

    WARNING:
	for now, recursionLocks are not unlocked when an image is
	restarted. You may have to recreate them to avoid a deadLock.
	(this may change in the future, but recreating a recursionLock in
	 the #earlyRestart handling does not hurt)

    [author:]
	Claus Gittinger

    [see also:]
	Semaphore
	Process ProcessorScheduler
"
!

examples
"
  example (good):
									[exBegin]
    |lock|

    lock := RecursionLock new.
    lock critical:[
	Transcript showCR:'in lock ...'.
	lock critical:[
	    Transcript showCR:'again ...'
	]
    ]
									[exEnd]

  in contrast to (wrong example - deadlocks):
									[exBegin]
    |lock|

    lock := Semaphore forMutualExclusion.
    lock critical:[
	Transcript showCR:'in lock ...'.
	lock critical:[
	    '*** never reached - deadlock because sema is already locked ***'.
	    '    (press CTRL-c and abort in the debugger)'.
	    Transcript showCR:'again ...'
	]
    ]
									[exEnd]
"
! !

!RecursionLock class methodsFor:'instance creation'!

forMutualExclusion
    "same as new, for easy exchangability with reular mutual-exclusion Semaphores."

    ^ self new 
!

new
    ^ self basicNew initialize

! !

!RecursionLock methodsFor:'printing & storing'!

displayOn:aGCOrStream
    "return a string to display the receiver - include the
     count for your convenience"

    "/ what a kludge - Dolphin and Squeak mean: printOn: a stream;
    "/ ST/X (and some old ST80's) mean: draw-yourself on a GC.
    (aGCOrStream isStream) ifFalse:[
        ^ super displayOn:aGCOrStream
    ].
    aGCOrStream
        nextPutAll:self class name;
        nextPut:$(.
    sema count printOn:aGCOrStream.
    aGCOrStream nextPutAll:' name: '.
    (self name ? 'unnamed') printOn:aGCOrStream.
    aGCOrStream nextPut:$).
!

name
    "return the semaphores userFriendly name"

    ^ sema name

    "Created: / 28.6.1997 / 16:19:40 / cg"
    "Modified: / 14.12.1999 / 21:03:46 / cg"
!

name:aString
    "set the semaphores userFriendly name"

    sema name:aString

    "Created: / 28.6.1997 / 16:19:47 / cg"
    "Modified: / 14.12.1999 / 21:03:52 / cg"
! !

!RecursionLock methodsFor:'private-initialization'!

initialize
    sema := Semaphore forMutualExclusion name:'recursionLock'

    "Modified: 25.1.1997 / 00:19:15 / cg"
! !

!RecursionLock methodsFor:'queries'!

numberOfWaitingProcesses
    "return the number of waiting processes"

    ^ sema numberOfWaitingProcesses

    "Created: 18.4.1996 / 17:18:08 / cg"
!

owner
    "return the owning processes (or nil)"

    ^ process
!

wouldBlock
    "Check if the resource represented by the receiver is  
     already in use by another process.
     Attention: if asked without some global lock (blockedInterrupts),
     the returned value may be outdated right away."

    ^ process notNil and:[Processor activeProcess ~~ process]
! !

!RecursionLock methodsFor:'signaling'!

signal
    |wasBlocked|

    process ~~ Processor activeProcess ifTrue:[
        "I have already got the lock"
        self error:'RecursionLock - signaling process doesn''t own the lock'.
    ].

    wasBlocked := OperatingSystem blockInterrupts.
    process := nil.
    sema signal.
    wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
! !

!RecursionLock methodsFor:'waiting'!

critical:aBlock
    "evaluate aBlock as a critical region, but do not block,
     if this lock is already held by the current process."

    |active retVal wasBlocked gotSema|

    active := Processor activeProcess.
    process == active ifTrue:[
        "I have already got the lock"
        ^ aBlock value
    ].

    "/
    "/ sema wait & process := active
    "/ and:
    "/ proces := nil & sema signal
    "/ must both be done atomic
    "/ Scenario:
    "/   ... recLock critical
    "/         got lock
    "/         evaluated
    "/         set process to nil
    "/         -> timer interrupt
    "/              recLock critical in timeOut action
    "/              process isNil
    "/                 sema wait !!!!!! DEADLOCK
    "/
    wasBlocked := OperatingSystem blockInterrupts.
    [
        (process notNil and:[process isDead]) ifTrue:[
            'RecursionLock [warning]: cleanup lock from dead process' infoPrintCR.
            process := nil. sema signal.
        ].
        gotSema := sema wait.
        process := active.
        wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
        retVal := aBlock value.
    ] ifCurtailed:[
        "be careful - the unwind may occur both while waiting
         AND while evaluating the block"
        gotSema notNil ifTrue:[
            OperatingSystem blockInterrupts.
            process := nil.
            sema signal.
        ].
        wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
    ].
    OperatingSystem blockInterrupts.
    process := nil.
    sema signal.
    wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
    ^ retVal.
!

critical:aBlock ifBlocking:blockingBlock
    "like critical:, but do not block if the lock cannot be aquired.
     Instead, return the value of the second argument, blockingBlock."

    ^ self critical:aBlock timeoutMs:0 ifBlocking:blockingBlock.
!

critical:aBlock timeoutMs:timeoutMs ifBlocking:blockingBlock
    "like critical:, but do not block if the lock cannot be aquired 
     within timeoutMs milliseconds.
     Instead, return the value of blockingBlock."

    |active retVal wasBlocked gotSema|

    active := Processor activeProcess.
    process == active ifTrue:[
        "I have already got the lock"
        ^ aBlock value
    ].

    "/
    "/ sema wait & process := active
    "/ and:
    "/ proces := nil & sema signal
    "/ must both be done atomic
    "/ Scenario:
    "/   ... recLock critical
    "/         got lock
    "/         evaluated
    "/         set process to nil
    "/         -> timer interrupt
    "/              recLock critical in timeOut action
    "/              process isNil
    "/                 sema wait !!!!!! DEADLOCK
    "/
    wasBlocked := OperatingSystem blockInterrupts.
    [
        (process notNil and:[process isDead]) ifTrue:[
            'RecursionLock [warning]: cleanup lock from dead process' infoPrintCR.
            process := nil. sema signal.
        ].
        gotSema := sema waitWithTimeoutMs:timeoutMs.
        gotSema notNil ifTrue:[
            process := active.
            wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
            retVal := aBlock value.
        ].
    ] ifCurtailed:[
        "be careful - the unwind may occur both while waiting
         AND while evaluating the block"
        gotSema notNil ifTrue:[
            OperatingSystem blockInterrupts.
            process := nil.
            sema signal.
        ].
        wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
    ].
    gotSema notNil ifTrue:[
        OperatingSystem blockInterrupts.
        process := nil.
        sema signal.
        wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
    ] ifFalse:[
        wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
        retVal := blockingBlock value.
    ].

    ^ retVal.
!

wait
    "wait, but do not block,
     if this lock is already held by the current process.
     Answer false, if alread locked, true if lock has been just acquired."

    |active wasBlocked|

    active := Processor activeProcess.
    process == active ifTrue:[
        "I have already got the lock"
        ^ false.
    ].

    wasBlocked := OperatingSystem blockInterrupts.
    (process notNil and:[process isDead]) ifTrue:[
        'RecursionLock [warning]: cleanup lock from dead process' infoPrintCR.
        process := nil. sema signal.
    ].
    sema wait.
    process := active.
    wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
    ^ true.
! !

!RecursionLock class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic/RecursionLock.st,v 1.41 2014-10-21 19:19:55 stefan Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libbasic/RecursionLock.st,v 1.41 2014-10-21 19:19:55 stefan Exp $'
! !