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.

    1. 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.

    2. 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.

    3. 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*

    1. Unguarded methods can always run concurrently with any other method of 
the same scope.

    2. 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
   11guard 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

Reply via email to