JavaMonitor.st
author Claus Gittinger <cg@exept.de>
Sun, 23 Feb 2020 14:03:15 +0100
branchcvs_MAIN
changeset 3997 5bb44f7e1d20
parent 3727 e506dabffeae
permissions -rw-r--r--
#REFACTORING by exept class: Java class changed: #dumpConfigOn:

"
 COPYRIGHT (c) 1996-2015 by Claus Gittinger

 New code and modifications done at SWING Research Group [1]:

 COPYRIGHT (c) 2010-2015 by Jan Vrany, Jan Kurs and Marcel Hlopko
                            SWING Research Group, Czech Technical University in Prague

 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.

 [1] Code written at SWING Research Group contains a signature
     of one of the above copright owners. For exact set of such code,
     see the differences between this version and version stx:libjava
     as of 1.9.2010
"
"{ Package: 'stx:libjava' }"

"{ NameSpace: Smalltalk }"

Object subclass:#JavaMonitor
	instanceVariableNames:'owningProcess processesEntered monitorSema count waitingSema
		processesWaiting waitEnabled ownerPrintString'
	classVariableNames:'instVarAccess'
	poolDictionaries:''
	category:'Languages-Java-Support'
!

!JavaMonitor class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1996-2015 by Claus Gittinger

 New code and modifications done at SWING Research Group [1]:

 COPYRIGHT (c) 2010-2015 by Jan Vrany, Jan Kurs and Marcel Hlopko
                            SWING Research Group, Czech Technical University in Prague

 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.

 [1] Code written at SWING Research Group contains a signature
     of one of the above copright owners. For exact set of such code,
     see the differences between this version and version stx:libjava
     as of 1.9.2010

"
! !

!JavaMonitor class methodsFor:'initialization'!

initialize
    instVarAccess := RecursionLock forMutualExclusion.

    "Created: / 29-11-2011 / 11:23:56 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified (format): / 09-01-2013 / 16:10:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitor class methodsFor:'instance creation'!

for: owningObject
    ^ self basicNew initializeFor: owningObject.

    "Created: / 30-11-2011 / 20:39:55 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

for: owningObject thread: threadOrNil nestedLockCount: count
    ^ self basicNew initializeFor: owningObject thread: threadOrNil nestedLockCount: count

    "Created: / 26-08-2012 / 17:01:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitor methodsFor:'accessing'!

activeProcess
    ^ Processor activeProcess
!

copyProcessesEntered
    ^ processesEntered copy.

    "Created: / 20-11-2011 / 14:45:24 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

count
    ^ count
!

processesEntered
    "dont do anything to me, access needs to be sync'd"
    ^ processesEntered.

    "Created: / 20-11-2011 / 13:22:15 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
! !

!JavaMonitor methodsFor:'atomic'!

addProcess: aProcess 
    instVarAccess critical: [ processesEntered add: aProcess ].

    "Created: / 20-11-2011 / 20:30:07 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

clearOwningProcess
    instVarAccess critical: [ owningProcess := nil ].

    "Created: / 20-11-2011 / 20:34:27 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

decrement
    "owning process released monitor, let's lower our counter so we know how many times
     we have to release it"

    "/ the lock is probably not helping here to protect against problems with
    "/ increment/decrement:
    "/ the write-access (in reinitCounter) will be either done before incrementing/decrementing or after.
    "/ if after, everything is OK;
    "/ if before, the decremented value will be zero, which is probably wrong.

    "/ without the critical region, we might also get possible inconsistencies:
    "/ if written before, the counter will drop to zero
    "/ if written in-between, the counter will have the old value decremented;
    "/ if written afterwards, everything is OK.

    "/ so, the lock should be really elsewhere, making sure that noone will count
    "/ the monitor at-all and the monitor is reinitialized without anyone depending on the
    "/ count at all.
    "/ but then, no critical section is needed in reinitCounter!!.

    instVarAccess critical: [ count := count - 1 ].

    "Created: / 22-11-2011 / 10:49:33 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified (comment): / 19-06-2017 / 14:39:51 / mawalch"
!

disableWait
    JavaVM monitorTrace ifTrue:[
        Logger log: ('Waiting is disabled on monitor for %1' bindWith: ownerPrintString) severity:Logger severityDEBUG facility:#JVM.
    ].
    "/ critical region not needed here
    "/ instVarAccess critical: [ waitEnabled := false ].
    waitEnabled := false.

    "Created: / 30-11-2011 / 20:34:40 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 02-03-2015 / 14:06:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

enableWait
    "/ critical region not needed here
    "/ instVarAccess critical: [waitEnabled := true].
    waitEnabled := true
    
    "Created: / 30-11-2011 / 20:34:31 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified (format): / 11-10-2013 / 11:17:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

increment
    "owning process entered monitor again (recursion ...), let's raise our counter so we know how many times
     we have to release it"

    "/ the lock is probably not helping here to protect against problems with
    "/ increment/decrement:
    "/ the write-access (in reinitCounter) will be either done before incrementing/decrementing or after.
    "/ if after, everything is OK;
    "/ if before, the decremented value will be zero, which is probably wrong.

    "/ without the critical region, we might also get possible inconsistencies:
    "/ if written before, the counter will drop to zero
    "/ if written in-between, the counter will have the old value decremented;
    "/ if written afterwards, everything is OK.

    "/ so, the lock should be really elsewhere, making sure that noone will count
    "/ the monitor at-all and the monitor is reinitialized without anyone depending on the
    "/ count at all.
    "/ but then, no critical section is needed in reinitCounter!!.

    instVarAccess critical: [ count := count + 1 ].

    "Created: / 22-11-2011 / 10:49:07 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 27-08-2012 / 10:42:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (comment): / 19-06-2017 / 14:56:26 / mawalch"
!

owningProcess: aProcess 
    instVarAccess critical: [ owningProcess := aProcess ].

    "Created: / 20-11-2011 / 20:32:26 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

processesWaitingAdd: aProcess 
    instVarAccess 
        critical: [
            self assert: (processesWaiting includesKey: aProcess) not.
            processesWaiting at: aProcess put: count
        ].

    "Created: / 22-11-2011 / 11:57:50 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

processesWaitingRestore: aProcess 
    instVarAccess 
        critical: [
            self assert: (processesWaiting includesKey: aProcess).
            self reinitCounter: (processesWaiting at: aProcess).
            processesWaiting removeKey: aProcess
        ].

    "Created: / 22-11-2011 / 12:59:14 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

reinitCounter
    "owning process is different from previous, let's start counting from beginning"

    "/ the lock is probably not helping here to protect against problems with
    "/ increment/decrement:
    "/ the write-access here will be either done before incrementing/decrementing or after.
    "/ if after, everything is OK;
    "/ if before, the decremented value will be zero, which is probably wrong.

    "/ without the critical region, we might also get possible inconsistencies:
    "/ if written before, the counter will drop to zero
    "/ if written in-between, the counter will have the old value decremented;
    "/ if written afterwards, everything is OK.

    "/ so, the lock should be really elsewhere, making sure that noone will count
    "/ the monitor at-all and the monitor is reinitialized without anyone depending on the
    "/ count at all.
    "/ but then, no critical section is needed!!.

    instVarAccess critical: [ count := 1 ].

    "Created: / 22-11-2011 / 10:51:09 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified (comment): / 19-06-2017 / 14:56:31 / mawalch"
!

reinitCounter: newCount
    "/ the lock is probably not helping here to protect against problems with
    "/ increment/decrement:
    "/ the write-access here will be either done before incrementing/decrementing or after.
    "/ if after, everything is OK;
    "/ if before, the decremented value will be zero, which is probably wrong.

    "/ without the critical region, we might also get possible inconsistencies:
    "/ if written before, the counter will drop to zero
    "/ if written in-between, the counter will have the old value decremented;
    "/ if written afterwards, everything is OK.

    "/ so, the lock should be really elsewhere, making sure that noone will count
    "/ the monitor at-all and the monitor is reinitialized without anyone depending on the
    "/ count at all.
    "/ but then, no critical section is needed!!.

    instVarAccess critical: [ count := newCount ].

    "Created: / 22-11-2011 / 13:00:51 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified (comment): / 19-06-2017 / 14:56:35 / mawalch"
!

removeProcess: aProcess 
    instVarAccess 
        critical: [
            self assert: processesEntered last == aProcess.
            processesEntered remove: aProcess
        ].

    "Created: / 20-11-2011 / 20:28:37 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

waitEnabled
    "/ critical region not needed here
    "/ instVarAccess critical: [ ^ waitEnabled].
    ^ waitEnabled.

    "Created: / 30-11-2011 / 20:34:56 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
! !

!JavaMonitor methodsFor:'initialization'!

initialize
    "Q: is this ever called?"
    
    owningProcess := nil.
    processesEntered := OrderedCollection new.
    monitorSema := Semaphore new: 1.
    processesWaiting := Dictionary new.
    waitingSema := Semaphore new: 0.
    waitEnabled := true.

    "Created: / 20-11-2011 / 13:28:28 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

initializeFor: owningObject
    self initializeFor: owningObject thread: 0 nestedLockCount: 0.

    "Created: / 30-11-2011 / 20:39:31 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 26-08-2012 / 17:25:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

initializeFor:owningObject thread:threadId nestedLockCount:cnt 

"/    self assert: (owningObject isJavaObject or:[owningObject isJavaClass]).

    processesEntered := OrderedCollection new.
    waitEnabled := true.
    processesWaiting := Dictionary new.
    waitingSema := Semaphore new:0.
    ownerPrintString := owningObject class name , '@' , owningObject identityHash printString.

    "/Not locked...    
    threadId == 0 ifTrue:[
        monitorSema := Semaphore new:1.
        count := 0.
        ^self.
    ] ifFalse: [
        "threadId is not zero (zero is threadId of scheduler process, which will never try to acquire the monitor)"
        "so it means it is possible that the thin lock is already locked for other thread, we must be careful " 

        owningProcess := ObjectMemory processesKnownInVM detect:[:p|p id == threadId] ifNone:[nil].
        self assert: owningProcess notNil.
        processesEntered add: owningProcess.  
        cnt timesRepeat:[
"/            JavaVM enteredMonitorsOf:  owningProcess add: owningObject.
"/            JavaVM acquiredMonitorsOf: owningProcess add: owningObject.
        ].
        monitorSema := Semaphore new: 0.
        count := cnt.
    ].

    "Created: / 26-08-2012 / 17:02:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitor methodsFor:'public'!

acquire
    | thisProcess |
    thisProcess := self activeProcess.
    (self isOwnedBy: thisProcess) ifTrue: [
        self increment.
        ^ self
    ].
    monitorSema wait.
    self owningProcess: thisProcess.
    count := 1.

    "Created: / 20-11-2011 / 13:21:46 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 27-08-2012 / 11:45:46 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

enter
    | thisProcess |
    thisProcess := self activeProcess.
    self addProcess: thisProcess.
    self acquire.

    "Created: / 20-11-2011 / 13:21:42 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

exit
    | thisProcess |
    thisProcess := self activeProcess.
    (self isOwnedBy: thisProcess) ifTrue: [ self release. ].    
    processesEntered remove: thisProcess ifAbsent: nil.

    "Created: / 20-11-2011 / 13:21:54 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 26-08-2012 / 19:54:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

notify
    "wakeup one waiting process"
    
    | thisProcess |
    thisProcess := Processor activeProcess.
    self assert: (self isOwnedBy: thisProcess).
    JavaVM monitorTrace ifTrue:[
        Logger 
            log: ('%1: notifying %2 processes' bindWith: thisProcess printString
                    with: processesWaiting size)
            severity: Logger severityDEBUG
            facility: #JVM.
    ].
    waitingSema signal.
    Processor yield.

    "Created: / 22-11-2011 / 12:14:23 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 02-03-2015 / 14:06:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

notifyAll
    "wakeup one waiting process"
    
    | thisProcess |
    thisProcess := Processor activeProcess.
    self assert: (self isOwnedBy: thisProcess).
    JavaVM monitorTrace ifTrue:[
        Logger 
            log: ('%1: notifying all %2 processes' bindWith: thisProcess printString
                    with: processesWaiting size)
            severity: Logger severityDEBUG
            facility: #JVM.
    ].
    waitingSema signalForAll.
    Processor yield.

    "Created: / 22-11-2011 / 12:14:36 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 02-03-2015 / 14:06:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

release
    self assert: (self isOwnedBy: owningProcess).
    count == 1 ifTrue: [
        self clearOwningProcess.
        count := 0.
        monitorSema signal.
    ] ifFalse: [
        self decrement.       
    ]

    "Created: / 20-11-2011 / 13:21:52 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 11-10-2013 / 11:18:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

releaseAll
    count timesRepeat: [self release].

    "Created: / 22-11-2011 / 13:05:20 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

wait
    "make owning process to go to wait"
    
    self waitForMilliseconds: nil.

    "Created: / 22-11-2011 / 11:57:56 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

waitForMilliseconds: timeOut 
    "make owning process to go to wait, but not longer than timeout"
    
    | thisProcess countBeforeWait |
    thisProcess := Processor activeProcess.
    self assert: (self isOwnedBy: thisProcess).
    self waitEnabled ifFalse: [
        JavaVM monitorTrace ifTrue:[
            Logger 
                log: ('%1 wanted to go to sleep, but it cant, this monitor is for %2 which is already dead' 
                        bindWith: thisProcess printString
                        with: ownerPrintString)
                severity: Logger severityDEBUG
                facility: #JVM.
        ].
        ^ self.
    ].
    JavaVM monitorTrace ifTrue:[
        Logger 
            log: ('%1 is going to wait on %3 for timeout: %2' 
                    bindWith: thisProcess printString
                    with: timeOut
                    with: ownerPrintString printString)
            severity: #debug
            facility: #JVM.
    ].
    self processesWaitingAdd: thisProcess.
    countBeforeWait :=  count.
    self releaseAll.
     "JV@2011-11-25: zero timeout means wait without timeout!!!!!!"
    timeOut == 0 ifTrue: [ waitingSema wait ] ifFalse: [
        waitingSema waitWithTimeoutMs: timeOut
    ].
    JavaVM monitorTrace ifTrue:[ 
        Logger 
            log: ('%1 has been notified and is trying to acquire monitor for %2 which is owned by %3' 
                    bindWith: thisProcess printString with: ownerPrintString printString with: owningProcess printString)
            severity: #debug
            facility: #JVM.
    ].
    self acquire.
    count := countBeforeWait.

    self processesWaitingRestore: thisProcess.

    "Created: / 22-11-2011 / 12:52:45 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 01-12-2011 / 10:57:52 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 02-03-2015 / 14:06:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitor methodsFor:'queries'!

isAcquired
    ^ instVarAccess critical: [ owningProcess notNil ].

    "Created: / 20-11-2011 / 13:22:26 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

isOwnedBy: aProcess 
    ^ instVarAccess critical: [ owningProcess == aProcess ].

    "Created: / 20-11-2011 / 13:23:14 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
! !

!JavaMonitor class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
!

version_HG

    ^ '$Changeset: <not expanded> $'
!

version_SVN
    ^ '$Id$'
! !


JavaMonitor initialize!