In case it helps here the version that does not deadlock (line # 11 does a "guard off") with the annotated trace log.

Here "solved.rex":

     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 -- now no deadlock anymore !?!
   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

Here the trace log:

   [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 -- now no deadlock anymore !?!
   [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"

Again, the question whether this behaviour is due to a bug in ooRexx or whether there is a rule that explains this behaviour?

Why should releasing the guard lock before invoking method M1 solve the deadlock when entering later method M2 from a routine context?

Or with yet other words: releasing the guard lock in method M1 using "guard off" should allow method M2 later to acquire it no matter from where it gets invoked?

---rony


On 05.07.2024 16:38, Rony G. Flatscher wrote:

A question related to the following simple program which Jean Louis has come up with (cf. <https://github.com/jlfaucher/executor/blob/master/sandbox/jlf/samples/concurrency/deadlock1.rex>) some time ago and which I edited a little bit to ease reading the trace lines:

      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

Running the above program will end in a deadlock.

Using another program that collects the trace objects employing "::options trace results" while running the above program on a different thread (thereby not getting deadlocked itself) and afterwords annotating the trace log objects created while running the above program in the following manner: if the current guard state differs from the defined one the effective guard state is shown in lowercase letters ('u') in the extended bracketed prefix, if an instruction is waiting (e.g. on the gaurd lock) then it gets marked with a "W" right before the closing bracket; then the following trace lines using a customized makeString method reflect all this information:

    [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 prolog code in I2 on T2 creates an instance of CLZ (line # 1) which causes an invocation I3 on T2 entering the INIT method which acquires the guard lock. The reply keyword statement in line # 10 causes the return from that invocation and the remaining INIT instructions of invocation I3 get dispatched on a new thread T3. Sending the M1 message to the newly created object is carried out in I4, the method M1 gets successfully the guard lock, releasing it thereafter in line # 17 with the "guard off" keyword statement.

At that point I3 is waiting on the "self~m1" message to return, I4 is continuing unguarded. The "guard on when a <> 1" statement in line # 19 causes the condition "a <> 1" to be evaluated after gaining the guard lock (see the trace with the prefix >K>) but does not turn .true in this case. So the guard lock for I4 should be released again (does this happen?).

As long as the attribute "a" in the condition is not changed by a method running on another thread, the WHEN condition does not get re-evaluated.

In order to change the attribute "a" in I2 on T2 (line # 3) the message M2 gets sent to the newly created object causing a new invocation I5 in which method M2 (line # 22) gets entered. It seems that M2 is not able to acquire the guard lock as it gets blocked at that point in time and the program gets deadlocked.

Is this to be expected? If so, why, what is the rule for this?

---

Adding a "guard off" in line # 11 in I3 on T3 right *before* the statement "self~m1" makes it possible for method M2 way later in I5 on T2 to acquire the guard lock! This then enables I5 on T2 to execute "a = 2", hence changing the value of attribute "a" such that the next time the WHEN condition in line # 19 gets re-evaluated in I4 on T3 will turn .true allowing method M1 in I4 on T3 to conclude. As a result all threads conclude and the program can end without a deadlock.

Why is it, that that change makes this program run successfully to the end?

---rony
_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to