Guten Tag Rony +1 globally
I still continue to think that the point 1 for guarded methods could be reworded. Your description makes reference to 2 guarded methods: the “caller”: A guarded method can invoke... the “called”: If that other method is guarded... In the point 2, it’s clear that the focus is on the “called”: Invoking a guarded method from another thread The purpose of the point 1 is to explain when the counter is incremented. That doesn’t depend on the “caller” being guarded, but it depends on the thread of the caller. Maybe the point 1 should start like that: Invoking a guarded method from the same thread … Sorry, I don’t have time to rewrite the full point. Just sharing my feeling And not asking you to modify the point 1. > On 10 Jul 2024, at 13:27, Rony G. Flatscher <rony.flatsc...@wu.ac.at> wrote: > > O.K. after quite some research and inspection time these seem to be the rules: > > Rules for running guarded methods > > ooRexx ensures that guarded methods of the same scope (defined for the same > class) cannot execute concurrently to protect access to its attribute pool > (object variable pool). To do so, a counter-based guard lock is maintained > for each scope, which gets increased by one if a guarded method gets invoked > and decreased by one upon return. If a guarded method for the same scope gets > invoked from another thread and the scope's guard lock counter is not zero, > it gets blocked because another guarded method holding the guard lock is > currently running in the same scope. Once the scope's guard lock counter > drops to 0 (no other guarded method runs currently), the blocked guarded > method can acquire the guard lock, thereby increasing the guard lock counter > to one and starting to run. > > A guarded method can invoke any other method belonging to the same scope on > its thread, be it guarded or unguarded. If that other method is guarded, then > it will increase the guard lock counter by one and, upon return, will > decrease it by one. > > Invoking a guarded method from another thread for a scope in which a guarded > method is currently running will block the invocation until the scope's guard > lock counter drops to 0 (no other guarded method for the same scope is > running anymore) and the guard lock becomes free. In this situation, the > blocked guarded method will succeed in acquiring the guard lock, increasing > the guard lock counter to one, and starting running. > > If a REPLY keyword statement is processed in a currently guarded method, the > remaining instructions of the guarded method will remain guarded. > > Rules for running unguarded methods > > Unguarded methods can always run concurrently with any other method of the > same scope. > > If a REPLY keyword statement is processed in a currently unguarded method, > the remainder of the invocation will continue on a different thread, also > unguarded. > > ----------------------------------------------------------------------------------------- > Here analyzing the "deadlock.rex" example according to these rules: > > Code: > > 1 o = .Clz ~new > 2 call syssleep 0.5 > 3 o~m2 −− wake −up m1 > 4 say " done . " > 5 > 6 ::class Clz > 7 ::method init −− guarded method > 8 expose a −− exclusive access > 9 a = 0 > 10 reply > 11 > 12 self~m1 −− still guarded > 13 > 14 ::method m1 −− guarded method > 15 expose a −− exclusive access > 16 a = 1 > 17 guard off −− unguard m1 method > 18 say "m1 method : unguarded " > 19 guard on when a <> 1 > 20 say "m1 method : guarded " > 21 > 22 ::method m2 −− guarded method > 23 expose a −− exclusive access > 24 a = 2 −− change attribute > > Extended trace output: > > [T2 I2] >I> Routine "deadlock.rex" > [T2 I2] 1 *-* o = .Clz~new > [T2 I3 G A1 L0 ] >I> Method "INIT" with scope "CLZ" > [T2 I3 G A1 L1 * ] 8 *-* expose a -- exclusive access > [T2 I3 G A1 L1 * ] 9 *-* a = 0 > [T2 I3 G A1 L1 * ] >>> "0" > [T2 I3 G A1 L1 * ] 10 *-* reply > [T2 I3 G A1 L1 * ] <I< Method "INIT" with scope "CLZ" > [T2 I2] >>> "a CLZ" > [T2 I2] 2 *-* call syssleep 0.5 > [T3 I3 G A1 L1 * ] >I> Method "INIT" with scope "CLZ" > [T3 I3 G A1 L1 * ] 12 *-* self~m1 -- still guarded > [T3 I4 G A1 L1 ] >I> Method "M1" with scope "CLZ" > [T3 I4 G A1 L2 * ] 15 *-* expose a -- exclusive access > [T3 I4 G A1 L2 * ] 16 *-* a = 1 > [T3 I4 G A1 L2 * ] >>> "1" > [T3 I4 G A1 L2 * ] 17 *-* guard off -- unguard m1 method > [T3 I4 G u A1 L1 ] 18 *-* say "m1 method: unguarded" > [T3 I4 G u A1 L1 ] >>> "m1 method: unguarded" > [T3 I4 G u A1 L1 ] 19 *-* guard on when a <> 1 > [T3 I4 G u A1 L2 * W] >K> "WHEN" => "0" > [T2 I2] >>> "0" > [T2 I2] 3 *-* o~m2 -- wake-up m1 > [T2 I5 G A1 L1 W] >I> Method "M2" with scope "CLZ" > > The message "o~m2" will cause a wait (block) as upon entry to "M2" from a > different thread (T2) the lock count is not 0, but 1 (L1) such that it needs > to wait until the guard lock counter drops to 0. > > ----------------------------------------------------------------------------------------- > > Here analyzing the "solved.rex" example according to these rules: > Code: > > 1 o = .Clz ~new > 2 call syssleep 0.5 > 3 o~m2 −− wake −up m1 > 4 say " done . " > 5 > 6 ::class Clz > 7 ::method init −− guarded method > 8 expose a −− exclusive access > 9 a = 0 > 10 reply > 11 guard off -- free guard lock > 12 self~m1 −− still guarded > 13 > 14 ::method m1 −− guarded method > 15 expose a −− exclusive access > 16 a = 1 > 17 guard off −− unguard m1 method > 18 say "m1 method : unguarded " > 19 guard on when a <> 1 > 20 say "m1 method : guarded " > 21 > 22 ::method m2 −− guarded method > 23 expose a −− exclusive access > 24 a = 2 −− change attribute > > > Extended trace output: > > [T2 I2] >I> Routine "solved.rex" > [T2 I2] 1 *-* o = .Clz~new > [T2 I3 G A1 L0 ] >I> Method "INIT" with scope "CLZ" > [T2 I3 G A1 L1 * ] 8 *-* expose a -- exclusive access > [T2 I3 G A1 L1 * ] 9 *-* a = 0 > [T2 I3 G A1 L1 * ] >>> "0" > [T2 I3 G A1 L1 * ] 10 *-* reply > [T2 I3 G A1 L1 * ] <I< Method "INIT" with scope "CLZ" > [T2 I2] >>> "a CLZ" > [T2 I2] 2 *-* call syssleep 0.5 > [T3 I3 G A1 L1 * ] >I> Method "INIT" with scope "CLZ" > [T3 I3 G A1 L1 * ] 11 *-* guard off -- free guard lock > [T3 I3 G u A1 L0 ] 12 *-* self~m1 -- now unguarded > [T3 I4 G A1 L0 ] >I> Method "M1" with scope "CLZ" > [T3 I4 G A1 L1 * ] 15 *-* expose a -- exclusive access > [T3 I4 G A1 L1 * ] 16 *-* a = 1 > [T3 I4 G A1 L1 * ] >>> "1" > [T3 I4 G A1 L1 * ] 17 *-* guard off -- unguard m1 method > [T3 I4 G u A1 L0 ] 18 *-* say "m1 method: unguarded" > [T3 I4 G u A1 L0 ] >>> "m1 method: unguarded" > [T3 I4 G u A1 L0 ] 19 *-* guard on when a <> 1 > [T3 I4 G u A1 L1 * W] >K> "WHEN" => "0" > [T2 I2] >>> "0" > [T2 I2] 3 *-* o~m2 -- wake-up m1 > [T2 I5 G A1 L0 ] >I> Method "M2" with scope "CLZ" > [T2 I5 G A1 L1 * ] 23 *-* expose a -- exclusive access > [T2 I5 G A1 L1 * ] 24 *-* a = 2 -- change attribute > [T2 I5 G A1 L1 * ] >>> "2" > [T2 I5 G A1 L0 ] <I< Method "M2" with scope "CLZ" > [T2 I2] 4 *-* say "done." > [T2 I2] >>> "done." > [T3 I4 G u A1 L1 * ] >K> "WHEN" => "1" > [T3 I4 G A1 L1 * ] 20 *-* say "m1 method: guarded" > [T3 I4 G A1 L1 * ] >>> "m1 method: guarded" > [T2 I2] <I< Routine "solved.rex" > [T3 I4 G A1 L0 ] <I< Method "M1" with scope "CLZ" > [T3 I3 G u A1 L0 ] <I< Method "INIT" with scope "CLZ" > > The "guard off" statement in line 11 frees the guard lock turning the guarded > method into an unguarded method thereby reducing the lock count by one, such > that it drops to 0 (L0 for A1). As a result, later the message "o~m2" will > not block anymore as upon entry to "M2" from a different thread (T2) the lock > count is 0 (L0 for A1) and the guard lock can be successfully acquired > increasing the lock count to L1 for A1. The attribute "a" will get changed, > such that upon return the "guard on when a <> 1" gets re-evaluated upon exit > from method "M2" and this time turning .true ("WHEN" => "1") allowing "M1" to > continue to run and to conclude. The program ends with no deadlocked threads. > > ----------------------------------------------------------------------------------------- > > Some notes: > the extended trace log includes the attribute pool (object variable pool) ID > which allows for distinguishing different instances for which the methods run, > the extended trace log includes a hint in the case that a method is not > executing in the defined state due to the use of GUARD OFF|ON. In the trace > log above a guarded method that runs unguarded is marked with a small 'u' > right next to the method's defined state 'G', > the extended trace log includes a hint 'W' (for waiting) for trace output > where a wait for the guard lock takes place. > It is planned to change .TraceLog's 'Standard' option to include some of this > helpful information in the extended (bracketed) trace prefix. > > Any comments? > > ---rony > > > _______________________________________________ > Oorexx-devel mailing list > Oorexx-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/oorexx-devel
_______________________________________________ Oorexx-devel mailing list Oorexx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/oorexx-devel