Has anyone used the heavyweight (Mesa) Monitor? I want to try the conditional waiting, but keep getting a 'Monitor access violation'. I think its actually a bug in the code; its signaling code is performing #checkOwnerProcess, but this should only be done by the 'wait' code (which it is).
The disconcerting part of this is; I don't see how Monitor can work at all this way. There is no test case in the 3.8 image and I've found no example usages which could help prove me wrong. I wrote a test case demonstrating how I think it can be used. Running the test results in a Monitor access violation. But if I remove the call to #checkOwnerProcess in the signal method (my proposed fix) the test works fine. So, does anyone have any experience with the heavyweight (Mesa) Monitor and how did it work for you? Does this test represent misuse or is there really an issue with Monitor? testConditionalWaits "Start two background processes that build an OrderedCollection with the numbers 1 to 1000. The 'fives' process will only add numbers modulo 5 = 0 (i.e., 5, 10, 15, 20, ...) whereas the 'nonFives' process adds all the others. A third monitor waits until the goal is reached before asserting the goal was reached successfully." | goal work fives nonFives counter goalReached goalMonitor | goal _ (1 to: 1000) asOrderedCollection. work _ OrderedCollection new. fives _ Monitor new. nonFives _ Monitor new. counter _ 0. goalReached _ false. goalMonitor _ Monitor new. [ fives critical: [ [ fives waitUntil: [ counter \\ 5 = 0 ]. goalReached or: [ work add: (counter _ counter+1). nonFives signal. goalReached _ counter >= goal size ] ] whileFalse. goalMonitor signal ] ] fork. [ nonFives critical: [ [ nonFives waitWhile: [ counter \\ 5 = 0 ]. goalReached or: [ work add: (counter _ counter+1). fives signal. goalReached _ counter >= goal size ] ] whileFalse. goalMonitor signal ] ] fork. goalMonitor critical: [ goalMonitor waitUntil: [ goalReached ]. self assert: goal = work ] Thanks.. |
Hello, Attached you find the relevant page from my MVC Tutorial that contains four versions of a producer/consumer example. The comment of the Monitor class says that you can signal only from within the critical section of a monitor. It is therefore not allowed to write: monitor1 critical: [" do something " monitor2 signal ]. The following examples demonstrate that you can (and should) use one single monitor to do all synchronization. (This is the essential difference to semaphores. When you synchronize processes with semaphores, you will usually need more than one sema.) And here is a working version of your example. You can try it immediately in a workspace (with 'inspect'). | producer1 producer2 monitor goal work counter goalReached finished | goal _ (1 to: 1000) asOrderedCollection. work _ OrderedCollection new. counter := 0. goalReached := false. finished := Semaphore new. monitor := Monitor new. producer1 := [ [monitor critical: [monitor waitUntil: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. finished signal. ] . producer2 := [ [monitor critical: [monitor waitWhile: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. finished signal ] . producer1 forkAt: Processor userBackgroundPriority. producer2 forkAt: Processor userBackgroundPriority. finished wait; wait. goal = work. ---------------------------------------------------------------- Here is a second version that does not use a semaphore to inform the forking process about termination of both forked processes: | producer1 producer2 monitor goal work counter goalReached activeProducers| goal _ (1 to: 1000) asOrderedCollection. work _ OrderedCollection new. counter := 0. goalReached := false. activeProducers := 0. monitor := Monitor new. producer1 := [ monitor critical: [activeProducers := activeProducers + 1]. [monitor critical: [monitor waitUntil: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. " Transcript show: 'P1 '; show: counter printString; show: ' '; show: activeProducers printString; cr." goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. monitor critical: [activeProducers := activeProducers - 1. monitor signal: #finish]. ] . producer2 := [monitor critical: [activeProducers := activeProducers + 1]. [monitor critical: [monitor waitWhile: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. "Transcript show: 'P2 '; show: counter printString; show: ' '; show: activeProducers printString; cr." goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. monitor critical: [activeProducers := activeProducers - 1. monitor signal: #finish]. ] . producer1 forkAt: Processor userBackgroundPriority. producer2 forkAt: Processor userBackgroundPriority. monitor critical: [monitor waitUntil: [" Transcript show: 'Test finish cond'; show: activeProducers printString; cr. " activeProducers = 0 & (goalReached)] for: #finish. ]. goal = work ------------- Hope this helps. Boris Monitor.zip (4K) Download Attachment |
In reply to this post by Chris Muller
Hello again, Chris,
your example is not that bad; your only mistake is that you signal a foreign monitor without entering its critical region. You have to replace goalMonitor signal. with: goalMonitor critical: [goalMonitor signal]. and in the same way fives signal. with: fives critical: [fives signal]. nonFives signal. with: nonFives critical: [nonFives signal]. The following works: | goal work fives nonFives counter goalReached goalMonitor | goal _ (1 to: 1000) asOrderedCollection. work _ OrderedCollection new. fives _ Monitor new. nonFives _ Monitor new. counter _ 0. goalReached _ false. goalMonitor _ Monitor new. [ fives critical: [ [ fives waitUntil: [ counter \\ 5 = 0 ]. goalReached or: [ work add: (counter _ counter+1). nonFives critical: [nonFives signal]. goalReached _ counter >= goal size ] ] whileFalse. goalMonitor critical: [goalMonitor signal] ] ] fork. [ nonFives critical: [ [ nonFives waitWhile: [ counter \\ 5 = 0 ]. goalReached or: [ work add: (counter _ counter+1). fives critical: [fives signal]. goalReached _ counter >= goal size ] ] whileFalse. goalMonitor critical: [goalMonitor signal] ] ] fork. goalMonitor critical: [ goalMonitor waitUntil: [ goalReached ]. ]. work = goal ----------------------- Note that the process 'fives' adds the seqeuence 1, 6, 11 into your collection. To fix that, both processes should use the wait condition [ counter \\5 = 4]. Alternatively, the counter could start at 1 and be incremented after insertion into the collection. The correct expression for goalReached would then be: counter > goal size. Greetings, Boris |
In reply to this post by Chris Muller
That sure does clear it up for me. I now appreciate how this #checkOwnerProcess helps ensure proper usage..
Thanks Boris. - Chris ----- Original Message ---- From: Boris Gaertner <[hidden email]> To: The general-purpose Squeak developers list <[hidden email]> Sent: Sunday, March 19, 2006 5:08:54 AM Subject: Re: Looking for Monitor example Hello, Attached you find the relevant page from my MVC Tutorial that contains four versions of a producer/consumer example. The comment of the Monitor class says that you can signal only from within the critical section of a monitor. It is therefore not allowed to write: monitor1 critical: [" do something " monitor2 signal ]. The following examples demonstrate that you can (and should) use one single monitor to do all synchronization. (This is the essential difference to semaphores. When you synchronize processes with semaphores, you will usually need more than one sema.) And here is a working version of your example. You can try it immediately in a workspace (with 'inspect'). | producer1 producer2 monitor goal work counter goalReached finished | goal _ (1 to: 1000) asOrderedCollection. work _ OrderedCollection new. counter := 0. goalReached := false. finished := Semaphore new. monitor := Monitor new. producer1 := [ [monitor critical: [monitor waitUntil: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. finished signal. ] . producer2 := [ [monitor critical: [monitor waitWhile: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. finished signal ] . producer1 forkAt: Processor userBackgroundPriority. producer2 forkAt: Processor userBackgroundPriority. finished wait; wait. goal = work. ---------------------------------------------------------------- Here is a second version that does not use a semaphore to inform the forking process about termination of both forked processes: | producer1 producer2 monitor goal work counter goalReached activeProducers| goal _ (1 to: 1000) asOrderedCollection. work _ OrderedCollection new. counter := 0. goalReached := false. activeProducers := 0. monitor := Monitor new. producer1 := [ monitor critical: [activeProducers := activeProducers + 1]. [monitor critical: [monitor waitUntil: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. " Transcript show: 'P1 '; show: counter printString; show: ' '; show: activeProducers printString; cr." goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. monitor critical: [activeProducers := activeProducers - 1. monitor signal: #finish]. ] . producer2 := [monitor critical: [activeProducers := activeProducers + 1]. [monitor critical: [monitor waitWhile: [counter \\5 = 0]. goalReached or: [work add: (counter := counter + 1)]. "Transcript show: 'P2 '; show: counter printString; show: ' '; show: activeProducers printString; cr." goalReached := counter >= goal size. monitor signal ]. goalReached ] whileFalse. monitor critical: [activeProducers := activeProducers - 1. monitor signal: #finish]. ] . producer1 forkAt: Processor userBackgroundPriority. producer2 forkAt: Processor userBackgroundPriority. monitor critical: [monitor waitUntil: [" Transcript show: 'Test finish cond'; show: activeProducers printString; cr. " activeProducers = 0 & (goalReached)] for: #finish. ]. goal = work ------------- Hope this helps. Boris |
Free forum by Nabble | Edit this page |