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

Reply via email to