java monitors now know wait, notify, notifyall jk_new_structure
authorhlopkmar
Tue, 22 Nov 2011 12:04:31 +0000
branchjk_new_structure
changeset 1146 e458dd16772e
parent 1145 8728f5373a48
child 1147 9431cebfd873
java monitors now know wait, notify, notifyall
src/JavaMonitor.st
src/JavaMonitorsTests.st
src/stx_libjava.st
--- a/src/JavaMonitor.st	Tue Nov 22 10:12:20 2011 +0000
+++ b/src/JavaMonitor.st	Tue Nov 22 12:04:31 2011 +0000
@@ -43,7 +43,8 @@
 
 Object subclass:#JavaMonitor
 	instanceVariableNames:'owningProcess processesEntered monitorSema processesEnteredAccess
-		owningProcessAccess count countAccess'
+		owningProcessAccess count countAccess waitingSema
+		processesWaitingAccess processesWaiting'
 	classVariableNames:''
 	poolDictionaries:''
 	category:'Languages-Java-Support'
@@ -160,6 +161,24 @@
     "Created: / 20-11-2011 / 20:32:26 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 !
 
+processesWaitingAdd: aProcess 
+    processesWaitingAccess 
+        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 
+   processesWaitingAccess 
+   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, lets start counting from beginning"
     
@@ -168,8 +187,18 @@
     "Created: / 22-11-2011 / 10:51:09 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 !
 
+reinitCounter: newCount 
+    countAccess critical: [ count := newCount ].
+
+    "Created: / 22-11-2011 / 13:00:51 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
+!
+
 removeProcess: aProcess 
-    processesEnteredAccess critical: [ self assert: processesEntered last == aProcess. processesEntered remove: aProcess ].
+    processesEnteredAccess 
+        critical: [
+            self assert: processesEntered last == aProcess.
+            processesEntered remove: aProcess
+        ].
 
     "Created: / 20-11-2011 / 20:28:37 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 ! !
@@ -183,6 +212,9 @@
     processesEnteredAccess := Semaphore forMutualExclusion.
     monitorSema := Semaphore new: 1.
     countAccess := Semaphore forMutualExclusion.
+    processesWaiting := Dictionary new.
+    processesWaitingAccess := Semaphore forMutualExclusion.
+    waitingSema := Semaphore new: 0.
 
     "Created: / 20-11-2011 / 13:28:28 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 ! !
@@ -221,6 +253,28 @@
     "Created: / 20-11-2011 / 13:21:54 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 !
 
+notify
+    "wakeup one waiting process"
+    
+    | thisProcess |
+    thisProcess := Processor activeProcess.
+    self assert: (self isOwnedBy: thisProcess).        
+    waitingSema signal.
+
+    "Created: / 22-11-2011 / 12:14:23 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
+!
+
+notifyAll
+    "wakeup one waiting process"
+    
+    | thisProcess |
+    thisProcess := Processor activeProcess.
+    self assert: (self isOwnedBy: thisProcess).
+    waitingSema signalForAll.
+
+    "Created: / 22-11-2011 / 12:14:36 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
+!
+
 release
     | thisProcess |
     thisProcess := self activeProcess.
@@ -233,6 +287,39 @@
     ]
 
     "Created: / 20-11-2011 / 13:21:52 / Marcel Hlopko <hlopkmar@fel.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 |
+    thisProcess := Processor activeProcess.
+    self assert: (self isOwnedBy: thisProcess).
+    self processesWaitingAdd: thisProcess.
+    self releaseAll.
+    waitingSema waitWithTimeoutMs: timeOut.
+    Logger 
+        log: 'Process has been notified'
+        severity: #debug
+        facility: #JVM.
+    self acquire.
+    self processesWaitingRestore: thisProcess.
+
+    "Created: / 22-11-2011 / 12:52:45 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 ! !
 
 !JavaMonitor methodsFor:'queries'!
--- a/src/JavaMonitorsTests.st	Tue Nov 22 10:12:20 2011 +0000
+++ b/src/JavaMonitorsTests.st	Tue Nov 22 12:04:31 2011 +0000
@@ -42,7 +42,7 @@
 "{ Package: 'stx:libjava' }"
 
 TestCase subclass:#JavaMonitorsTests
-	instanceVariableNames:'result reason thisProcess assertionAccess'
+	instanceVariableNames:'result reason thisProcess assertionAccess thisProcessAccess'
 	classVariableNames:''
 	poolDictionaries:''
 	category:'Languages-Java-Tests-Synchronization'
@@ -185,6 +185,7 @@
     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>"
 !
@@ -202,8 +203,8 @@
 !
 
 waitForAndResumeThisProcess
-    self waitForStoppingThread: thisProcess.
-    thisProcess resume.
+    thisProcessAccess critical: [self waitForStoppingThread: thisProcess.
+    thisProcess resume.]
 
     "Created: / 20-11-2011 / 19:17:53 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 !
@@ -300,12 +301,12 @@
     t := [
                 count timesRepeat: [ mon enter ].
                 count timesRepeat: [
-                    self assert: (mon isOwnedBy: t).
+                    self assert: (mon isOwnedBy: t) message: 'mon was not owned by t1'.
                     mon exit
                 ].
                 self waitForAndResumeThisProcess
             ] newProcess.
-            t resume.
+    t resume.
     self stop.
     self validateResult.
 
@@ -318,19 +319,17 @@
     t1 := [
                 mon enter.
                 mon enter.
-                self assert: (mon isOwnedBy: t1).
+                self assert: (mon isOwnedBy: t1) message: 'mon was not owned by t1 after multiple enter'.
                 mon exit.
                 t1 stop.
-                self assert: (mon isOwnedBy: t1).
-                
+                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).
+                self assert: (mon isOwnedBy: t1) message: 'mon was not owned by t1 after stop'.
                 t1 resume.
             ] newProcess.
     t1 resume.
@@ -386,6 +385,137 @@
     "Created: / 20-11-2011 / 14:51:22 / Marcel Hlopko <hlopkmar@fel.cvut.cz>"
 ! !
 
+!JavaMonitorsTests methodsFor:'wait notify tests'!
+
+testManyThreadsWaitNotifyAll
+    | mon  threads |
+    mon := JavaMonitor new.
+    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 new.
+    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 new.
+    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>"
+!
+
+testOneThreadWaitTimeout
+    | mon  t |
+    mon := JavaMonitor new.
+    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_SVN
--- a/src/stx_libjava.st	Tue Nov 22 10:12:20 2011 +0000
+++ b/src/stx_libjava.st	Tue Nov 22 12:04:31 2011 +0000
@@ -204,18 +204,18 @@
      exclude individual packages in the #excludedFromPrerequisites method."
 
     ^ #(
-        #'squeak:petitparser'    "PPDelegateParser - superclass of JavaParser "
-        #'stx:goodies/sunit'    "TestCase - superclass of JavaMonitorsTests "
-        #'stx:libbasic'    "Array - superclass of JavaConstantPool "
-        #'stx:libbasic2'    "BitArray - superclass of extended BooleanArray "
+        #'squeak:petitparser'    "PPCompositeParser - superclass of JavaParser "
+        #'stx:goodies/sunit'    "TestAsserter - superclass of JavaMonitorsTests "
+        #'stx:libbasic'    "PeekableStream - superclass of JavaParser::LineNumberStream "
+        #'stx:libbasic2'    "Socket - superclass of JavaSocket "
         #'stx:libbasic3'    "MessageTracer - referenced by JavaMethod>>setBreakPoint "
         #'stx:libcomp'    "ObjectFileLoader - referenced by JavaVM class>>_Runtime_loadFileInternalI: "
         #'stx:libhtml'    "URL - referenced by JavaEmbeddedFrameView>>setupAppletFrameIn:initializeJava: "
-        #'stx:libtool'    "DebugView - referenced by Java class>>flushClasses "
-        #'stx:libview'    "GraphicsContext - superclass of JavaEmbeddedFrameView "
-        #'stx:libview2'    "GIFReader - referenced by JavaVM class>>_GifImageDecoder_parseImage: "
-        #'stx:libwidg'    "Button - referenced by JavaVM class>>_WButtonPeer_create: "
-        #'stx:libwidg2'    "ComboBoxView - referenced by JavaVM class>>processEvent: "
+        #'stx:libtool'    "WorkspaceApplication - referenced by JavaEvaluator>>evaluate:in:receiver:notifying:logged:ifFail: "
+        #'stx:libview'    "DeviceGraphicsContext - superclass of JavaPopUpView "
+        #'stx:libview2'    "Plug - referenced by JavaSourceCodeCache>>findMethodLine:inMethods: "
+        #'stx:libwidg'    "ScrollBar - referenced by JavaVM class>>_WScrollPanePeer__getVScrollbarWidth: "
+        #'stx:libwidg2'    "CheckBox - referenced by JavaVM class>>_WCheckboxPeer_create: "
     )
 ! !
 
@@ -574,6 +574,7 @@
         'GenericToolbarIconLibrary class' javaRuntimeExceptionBrowserIcon
         CharacterArray asDottedJavaClassName
         CharacterArray asSlashedJavaClassName
+        Process isWaiting
     )
 ! !
 
@@ -626,7 +627,7 @@
     "Return a SVN revision number of myself.
      This number is updated after a commit"
 
-    ^ "$SVN-Revision:"'1496'"$"
+    ^ "$SVN-Revision:"'1498'"$"
 ! !
 
 !stx_libjava class methodsFor:'file generation'!