orc-lang / orc

Orc programming language implementation
https://orc.csres.utexas.edu/
BSD 3-Clause "New" or "Revised" License
40 stars 3 forks source link

Vtime and termination do not behave as expected and cause an interpreter deadlock #190

Open arthurp opened 7 years ago

arthurp commented 7 years ago

Program

x <Some(x)<  Vclock(IntegerTimeOrder) >> (
      (Vawait(0) >> Some(f()) ; None() ) |
      Vawait(1) >> Some(g())
    ) 

Expected behavior

This program should execute f() and if it becomes quiescent, but not halted, the program will execute g(). If f() halts then None() will be published immediately without going quiescent. If either f() or g() publishes the whole thing should be killed. This means that if f() publishes without becoming quiescent (for instance f() = signal), then g() should never run.

Actual behavior

g() runs after f() publishes. And just to really rub it in the interpreter deadlocks.

The deadlock is a lock ordering problem. VirtualClock takes its own lock and then calls checkAlive on a handle which can take the lock of a group up the tree. Group kills take the Group lock and then call unsetQuiescent which takes the VirtualClock lock.

This issue appears to exist in both the released version and master.

Test program

-- Run g if f becomes idle/quiescent but not halted. Kill the whole mess when either half publishes.
def ifIdle[A](f :: lambda() :: A, g :: lambda() :: A) :: A = 
  x <Some(x)<  Vclock(IntegerTimeOrder) >> (
      (Vawait(0) >> Some(f()) ; None() ) |
      Vawait(1) >> Some(g())
    ) 

val c = Channel()

def test() = 
  ifIdle(lambda() = "Publish from left", lambda() = Println("FAIL: publication idle")) |
  stop

def f(n) if (n :> 0) = 
  test() >x> Println(x) >> stop ; Println("============") >> f(n-1)

f(1000)
arthurp commented 7 years ago

In initial work on trying to solve this (when I thought it might be simple) I made the following changes which might be useful to others: Patch.