JavaMonitorsTests.st
author Jan Vrany <jan.vrany@labware.com>
Sun, 09 Aug 2020 13:00:09 +0100
changeset 4004 794e6f425fa8
parent 3782 32e335ed7c6f
permissions -rw-r--r--
Merge

"
 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 }"

TestCase subclass:#JavaMonitorsTests
	instanceVariableNames:'result reason thisProcess assertionAccess thisProcessAccess'
	classVariableNames:''
	poolDictionaries:''
	category:'Languages-Java-Tests'
!

!JavaMonitorsTests 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

"
! !

!JavaMonitorsTests class methodsFor:'accessing'!

resources

  ^ Array 
        with: JavaInitializedResource 
        with: JavaLibrariesResource
        with: JavaTestsResource

    "Created: / 30-03-2012 / 13:38:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitorsTests methodsFor:'accessing'!

timeout
    ^60 * 5 "/5 min"

    "Created: / 03-12-2012 / 19:35:34 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitorsTests methodsFor:'mh exploratory tests'!

_testAssertInAnotherThreadShouldFail
    | t |
    "i thought so"
    t := [
        self assert: false message: 'I just want it to fail'.
        self waitForAndResumeThisProcess
    ] newProcess.
    t resume.
    self stop.
    self validateResult.

    "Created: / 26-08-2012 / 19:48:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testIssSemaphoreSignalledAutomaticallyOnStop
    | t1  t2  mon |
    mon := JavaMonitor for: self.
    t1 := [
                mon acquire.
                self assert: (mon owningProcess == t1) message: 'mon was not owned by t1 in t1'.
                t1 stop.
                mon release
            ] newProcess.
    t2 := [
                [ t1 isStopped ] whileFalse: [ Delay waitForMilliseconds: 100 ].
                self assert: (mon owningProcess == t1)
                    message: 'mon was not owned by t1 after t1 isStopped'.
                t1 resume.
                [ t1 isDead ] whileFalse: [ Delay waitForMilliseconds: 100. ].
                self waitForAndResumeThisProcess.
            ] newProcess.
    t1 resume.
    t2 resume.
    self stop.
    self validateResult.

    "Created: / 20-11-2011 / 17:28:16 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:17:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testNotifyDoesNotMeanAcquireWithoutChecking
    "aparently it does"
    
    "| t1  t2  mon |
    mon := JavaMonitor new.
    t1 := [
                mon enter.
                t1 stop.
                self assert: (mon isOwnedBy: t1)
                    message: 'monitor was not owned by t1 after resume'.
                mon exit
            ] newProcess.
    t2 := [
                self waitForStoppingThread: t1.
                self assert: (mon isOwnedBy: t1)
                    message: 'monitor was not owned by t1 after waiting for it to stop'.
                mon enter.
                self assert: (mon isOwnedBy: t2)
                    message: 'monitor was not owned by t2 after enter'.
                mon exit.
            ] newProcess.
    t1 resume.
    t2 resume.
    self waitForWaitingThread: t2.
    mon notify.
    t1 resume.
    self waitForDyingThread: t2.
    self validateResult."

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

!JavaMonitorsTests methodsFor:'multithreading testing support'!

assert: aBoolean message: message 
    assertionAccess critical: [
    result ifTrue: [
        result := aBoolean.
        reason := message.
    ].].

    "Created: / 20-11-2011 / 18:54:44 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

setUp
    result := true.
    reason := 'Everything went just fine'.
    thisProcess := Processor activeProcess.
    assertionAccess := Semaphore forMutualExclusion.
    thisProcessAccess := Semaphore forMutualExclusion.

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

stop
    thisProcess stop.

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

validateResult  
    super assert: result message: reason.

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

waitForAndResumeThisProcess
    thisProcessAccess critical: [self waitForStoppingThread: thisProcess.
    thisProcess resume.]

    "Created: / 20-11-2011 / 19:17:53 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

waitForDyingThread: aThread 
  
[ aThread isDead ] whileFalse: [ Delay waitForMilliseconds: 100 ].

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

waitForStoppingThread: aThread
    [ aThread isStopped ] whileFalse: [ Delay waitForMilliseconds: 100 ].

    "Created: / 20-11-2011 / 19:17:02 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
!

waitForWaitingThread: aThread 
    [ aThread isWaiting ] whileFalse: [ Delay waitForMilliseconds: 100 ].

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

!JavaMonitorsTests methodsFor:'tests'!

testBruteForceEnter
    | mon  threads  cleanupThread |
    mon := JavaMonitor for:self.
    threads := OrderedCollection new.
    20 timesRepeat: [
        | t |
        t := [
                    200 timesRepeat: [
                        mon acquire.
                        self assert: (mon owningProcess ==  t)
                            message: 'monitor was not owned by t after enter'.
                        Delay waitForMilliseconds: 2.
                        mon release.
                        self assert: (mon owningProcess ~~ t)
                            message: 'monitor was still owned by t after exit'.
                    ]
                ] newProcess.
        threads add: t
    ].
    cleanupThread := [
                threads do: [:each | self waitForDyingThread: each ].
                self waitForAndResumeThisProcess.
            ] newProcess.
    threads do: [:each | each resume ].
    cleanupThread resume.
    self stop.
    self validateResult.

    "Created: / 20-11-2011 / 19:37:28 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:12:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testOneThread
    | mon  thread |
    mon := JavaMonitor for:self.
    thread := [
                self assert: mon owningProcess isNil
                    message: 'monitor was acquired at the beginning'.
                mon acquire.
                self assert: mon owningProcess notNil
                    message: 'monitor was not acquired after monitor enter'.
                self assert: (mon owningProcess == Processor activeProcess)
                    message: 'somebody else owned monitor after monitor enter'.
                mon release.
                self assert: mon owningProcess isNil
                    message: 'monitor was still acquired after monitor release'.
                "/ JV@2017-06-03: No longer valid as JavaMonitor no longer maintains
                "/ list of 'entered' processed. Seems useless except of testing.
                "/ self assert: (mon processesEntered includes: Processor activeProcess)
                "/    message: 'thread was not in the processesEntered after release'.
                mon acquire.
                self assert: mon owningProcess notNil
                    message: 'monitor was not acquired after monitor acquire'.
                mon release.
                self assert: mon owningProcess isNil
                    message: 'monitor stayed acquired after monitor exit'.
                self assert: (mon processesEntered includes: Processor activeProcess) not
                    message: 'process was still in the processesEntered after monitorExit'.
                self waitForAndResumeThisProcess.
            ] newProcess.
    thread resume.
    self stop.
    self validateResult.

    "Created: / 20-11-2011 / 13:20:59 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:17:46 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testOneThreadMultipleTimes
    | mon  t  count |
    mon := JavaMonitor for:self.
    count := 5.
    t := [
                count timesRepeat: [ mon acquire ].
                count timesRepeat: [
                    self assert: (mon owningProcess == t) message: 'mon was not owned by t1'.
                    mon release
                ].
                self waitForAndResumeThisProcess
            ] newProcess.
    t resume.
    self stop.
    self validateResult.

    "Created: / 22-11-2011 / 11:02:42 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:17:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testTwoThreadMultipleTimes
    | mon  t1  t2 |
    mon := JavaMonitor for:self.
    t1 := [
                mon acquire.
                mon acquire.
                self assert: (mon owningProcess == t1)
                    message: 'mon was not owned by t1 after multiple enter'.
                mon release.
                t1 stop.
                self assert: (mon owningProcess == t1)
                    message: 'mon was not owned by t1 after single exit'.
                mon release.
                self waitForDyingThread: t2.
                self waitForAndResumeThisProcess
            ] newProcess.
    t2 := [
                self waitForStoppingThread: t1.
                self assert: (mon owningProcess == t1)
                    message: 'mon was not owned by t1 after stop'.
                t1 resume.
            ] newProcess.
    t1 resume.
    t2 resume.
    self stop.
    self assert: mon processesEntered isEmpty.
    self validateResult.

    "Created: / 22-11-2011 / 11:12:15 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:18:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testTwoThreads
    "this took me a while :)"
    
    | mon  t1  t2 |
    mon := JavaMonitor for:self.
    t1 := [
                t1 stop.
                mon acquire.
                t2 resume.
                t1 stop.
                self assert: (mon processesEntered includes: t2)
                    message: 't2 was not in processesEntered after entering monitor'.
                mon release.
                t1 stop.
                mon acquire.
                self assert: (mon owningProcess == t1)
                    message: 'monitor was not owned by t1 after t2 exitted it'.
                mon release.
            ] newProcess.
    t2 := [
                self waitForStoppingThread: t1.
                t1 resume.
                t2 stop.
                self assert: mon owningProcess notNil
                    message: 'monitor was not acquired after t1 entered it'.
                self assert: (mon isOwnedBy: t1)
                    message: 'monitor was not owned by t1 after it entered it'.
                t1 resume.
                mon acquire.
                self assert: (mon owningProcess == t2)
                    message: 'monitor was not owned by t2 after waiting on enter'.
                mon release.
                t1 resume.
                self waitForDyingThread: t1.
                self waitForAndResumeThisProcess
            ] newProcess.
    t1 resume.
    t2 resume.
    self stop.
    self validateResult.

    "Created: / 20-11-2011 / 14:51:22 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:18:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitorsTests methodsFor:'tests-java'!

test_releasing_01
    "This tests call Java code in following setup:
        - a java method with handler that calls
        - a java method with finally that calls
        - a __synchronized java method with that calls
        - a java method throwing RuntimeException

     When first Java method returns, all monitors should be released"
    
    | javaTests |
    javaTests := JAVA stx libjava tests lang MonitorTests new.
    self assert: (javaTests instVarNamed: #'_lockWord_') = 0.
    javaTests test_releasing_01.
    self assert: (javaTests instVarNamed: #token) == 1101101.
    self assert: (javaTests instVarNamed: #'_lockWord_') = 0.

    "Created: / 07-04-2012 / 08:22:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 12-04-2012 / 15:11:47 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 16-08-2017 / 22:44:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_releasing_02
    "This tests call Java code in following setup:
        - a smalltalk method (this one) with handler for RuntimeException
        - a java method with finally that calls
        - a __synchronized java method with that calls
        - a java method throwing RuntimeException

     When first Java method returns, all monitors should be released        
    "
    | javaTests caught |


    javaTests := JAVA stx libjava tests lang MonitorTests new.
    self assert: (javaTests instVarNamed: #'_lockWord_') = 0.
    caught := false.
    [
        javaTests instVarNamed: #token put: 3.
        javaTests test_releasing_01_finally.
    ] on: JAVA java lang RuntimeException do:[:ex|
        caught := true
    ].
    self assert: caught.
    self assert: (javaTests instVarNamed: #token) == 1003.
    self assert: (javaTests instVarNamed: #'_lockWord_') = 0.

    "Created: / 07-04-2012 / 08:39:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 16-08-2017 / 22:45:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitorsTests methodsFor:'wait notify tests'!

testManyThreadsWaitNotifyAll
    | mon  threads |
    mon := JavaMonitor for:self.
    threads := OrderedCollection new.
    2 timesRepeat: [
        | t |
        t := [
                    mon acquire.
                    self assert: (mon owningProcess ==  t)
                        message: 'thread was not owned by t after enter'.
                    self waitForAndResumeThisProcess.
                    'resumed thisProcess' infoPrintCR.
                    mon waitForMilliseconds: nil.
                    'notified and alive' infoPrintCR.
                    self assert: (mon owningProcess == t)
                        message: 'thread was not owned by t after wait'.
                    mon release.
                    'dying' infoPrintCR.
                ] newProcess.
        threads add: t.
    ].
    threads do: [
        :each | 
        'resuming t' infoPrintCR.
        each resume
    ].
    threads do: [
        :each | 
        'stopping' infoPrintCR.
        self stop
    ].
    threads do: [
        :each | 
        'waiting for waiting' infoPrintCR.
        self waitForWaitingThread: each
    ].
    'sync barrier reached' infoPrintCR.
    mon acquire.
    mon notify: true.
    'threads has been notified' infoPrintCR.
    mon release.
    'monitor exitted' infoPrintCR.
    threads do: [
        :each | 
        'waiting for them to die' infoPrintCR.
        self waitForDyingThread: each
    ].
    self validateResult.

    "Created: / 22-11-2011 / 12:27:16 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:22:09 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testOneThreadWaitMultipleEnters
    | mon  t |
    mon := JavaMonitor for:self.
    t := [
                mon acquire.
                mon acquire.
                mon acquire.
                self waitForAndResumeThisProcess.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after enter'.
                mon waitForMilliseconds: nil.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after wait'.
                mon release.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after wait'.
                mon release.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after wait'.
                mon release.
            ] newProcess.
    t resume.
    self stop.
    mon acquire.
    mon notify: false.
    mon release.
    self waitForDyingThread: t.
    self validateResult.

    "Created: / 22-11-2011 / 12:55:38 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:22:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testOneThreadWaitNotify
    | mon  t |
    mon := JavaMonitor for:self.
    t := nil.
    t := [
                mon acquire.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after enter'.
                self waitForAndResumeThisProcess.
                mon waitForMilliseconds: nil.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after wait'.
                self waitForAndResumeThisProcess
            ] newProcess.
    t resume.
    self stop.
    mon acquire.
    mon notify: false.
    mon release.
    self stop.
    self validateResult.

    "Created: / 22-11-2011 / 11:51:28 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:22:17 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testOneThreadWaitTimeout
    | mon  t |
    mon := JavaMonitor for:self.
    t := [
                mon acquire.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after enter'.
                
                "/notify will never come
                
                mon waitForMilliseconds: 500.
                self assert: (mon owningProcess == t)
                    message: 'thread was not owned by t after wait'.
            ] newProcess.
    t resume.
    self waitForDyingThread: t.
    self validateResult.

    "Created: / 22-11-2011 / 12:51:13 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 15-08-2017 / 22:15:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!JavaMonitorsTests class methodsFor:'documentation'!

version_CVS
    ^ '$Header: /cvs/stx/stx/libjava/JavaMonitorsTests.st,v 1.5 2015-03-20 12:08:00 vrany Exp $'
!

version_HG

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

version_SVN
    ^ 'Id'
! !