JavaMonitorsTests.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Fri, 24 May 2013 17:55:42 +0100
branchbuiltin-class-support
changeset 2629 cedb88626902
parent 2578 fc6186a4961f
child 2711 a00302fe5083
permissions -rw-r--r--
Closing branch.

"
 COPYRIGHT (c) 1996-2011 by Claus Gittinger

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

 COPYRIGHT (c) 2010-2011 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' }"

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

!JavaMonitorsTests class methodsFor:'documentation'!

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

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

 COPYRIGHT (c) 2010-2011 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 enter.
                self assert: (mon isOwnedBy: t1) message: 'mon was not owned by t1 in t1'.
                t1 stop.
                mon exit
            ] newProcess.
    t2 := [
                [ t1 isStopped ] whileFalse: [ Delay waitForMilliseconds: 100 ].
                self assert: (mon isOwnedBy: 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>"
!

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 enter.
                        self assert: (mon isOwnedBy: t)
                            message: 'monitor was not owned by t after enter'.
                        Delay waitForMilliseconds: 2.
                        mon exit.
                        self assert: (mon isOwnedBy: t) not
                            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>"
!

testOneThread
    | mon  thread |
    mon := JavaMonitor for:self.
    thread := [
                self assert: mon isAcquired not
                    message: 'monitor was acquired at the beginning'.
                mon enter.
                self assert: mon isAcquired
                    message: 'monitor was not acquired after monitor enter'.
                self assert: (mon isOwnedBy: Processor activeProcess)
                    message: 'somebody else owned monitor after monitor enter'.
                mon release.
                self assert: mon isAcquired not
                    message: 'monitor was still acquired after monitor release'.
                self assert: (mon processesEntered includes: Processor activeProcess)
                    message: 'thread was not in the processesEntered after release'.
                mon acquire.
                self assert: mon isAcquired
                    message: 'monitor was not acquired after monitor acquire'.
                mon exit.
                self assert: mon isAcquired not
                    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>"
!

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

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

testTwoThreadMultipleTimes
    | mon  t1  t2 |
    mon := JavaMonitor for:self.
    t1 := [
                mon enter.
                mon enter.
                self assert: (mon isOwnedBy: t1)
                    message: 'mon was not owned by t1 after multiple enter'.
                mon exit.
                t1 stop.
                self assert: (mon isOwnedBy: t1)
                    message: 'mon was not owned by t1 after single exit'.
                mon exit.
                self waitForDyingThread: t2.
                self waitForAndResumeThisProcess
            ] newProcess.
    t2 := [
                self waitForStoppingThread: t1.
                self assert: (mon isOwnedBy: 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>"
!

testTwoThreads
    "this took me a while :)"
    
    | mon  t1  t2 |
    mon := JavaMonitor for:self.
    t1 := [
                t1 stop.
                mon enter.
                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 isOwnedBy: t1)
                    message: 'monitor was not owned by t1 after t2 exitted it'.
                mon exit.
            ] newProcess.
    t2 := [
                self waitForStoppingThread: t1.
                t1 resume.
                t2 stop.
                self assert: mon isAcquired
                    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 enter.
                self assert: (mon isOwnedBy: t2)
                    message: 'monitor was not owned by t2 after waiting on enter'.
                mon exit.
                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>"
! !

!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 |
    self assert: (JavaVM enteredMonitorsOfProcess: thisProcess) isEmptyOrNil.
    javaTests := JAVA stx libjava tests lang MonitorTests new.
    javaTests test_releasing_01.
    self assert: (javaTests instVarNamed: #token) == 1101101.
    self assert: (JavaVM enteredMonitorsOfProcess: thisProcess) isEmptyOrNil.

    "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: / 06-07-2012 / 21:55:06 / 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 |

    self assert: (JavaVM enteredMonitorsOfProcess: thisProcess) isEmptyOrNil.

    javaTests := JAVA stx libjava tests lang MonitorTests new.
    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: (JavaVM enteredMonitorsOfProcess: thisProcess) isEmptyOrNil.

    "Created: / 07-04-2012 / 08:39:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_releasing_03
    "Checks for bugs in monitor handling, there used to be a bug
     that causes monitors not be entered/exited properly. This should
     catch this bug if it reappears
    "
    self assert: (JavaVM enteredMonitorsOfProcess: thisProcess) isEmptyOrNil.

    GroovyEvaluator new evaluate: '1'.

    self assert: (JavaVM enteredMonitorsOfProcess: thisProcess) isEmptyOrNil.

    "Created: / 07-04-2012 / 08:42:17 / 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 enter.
                    self assert: (mon isOwnedBy: t)
                        message: 'thread was not owned by t after enter'.
                    self waitForAndResumeThisProcess.
                    'resumed thisProcess' infoPrintCR.
                    mon wait.
                    'notified and alive' infoPrintCR.
                    self assert: (mon isOwnedBy: t)
                        message: 'thread was not owned by t after wait'.
                    mon exit.
                    '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 enter.
    mon notifyAll.
    'threads has been notified' infoPrintCR.
    mon exit.
    '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>"
!

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

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

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

    "Created: / 22-11-2011 / 11:51:28 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
    "Modified: / 24-07-2012 / 10:48:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testOneThreadWaitTimeout
    | mon  t |
    mon := JavaMonitor for:self.
    t := [
                mon enter.
                self assert: (mon isOwnedBy: t)
                    message: 'thread was not owned by t after enter'.
                
                "/notify will never come
                
                mon waitForMilliseconds: 500.
                self assert: (mon isOwnedBy: 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>"
! !

!JavaMonitorsTests class methodsFor:'documentation'!

version_CVS
    ^ '$Header: /cvs/stx/stx/libjava/JavaMonitorsTests.st,v 1.2 2013/02/25 11:15:31 vrany Exp $'
!

version_HG

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

version_SVN
    ^ '§Id§'
! !