Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

change the error returned by Instance::run etc. on deadlock#155

Merged
dicej merged 2 commits into
mainfrom
dicej/fix-152
May 5, 2025
Merged

change the error returned by Instance::run etc. on deadlock#155
dicej merged 2 commits into
mainfrom
dicej/fix-152

Conversation

@dicej
Copy link
Copy Markdown
Collaborator

@dicej dicej commented May 2, 2025

Previously, we returned an "async-lifted export failed to return a result" which was not always accurate and usually misleading.

This also adds documentation to Instance::run explaining when and why a "deadlock" error will be returned.

Note that the new backpressure-deadlock.wast test was copied from Alex Crichton's issue description.

Fixes #152

Previously, we returned an "async-lifted export failed to return a result" which
was not always accurate and usually misleading.

This also adds documentation to `Instance::run` explaining when and why a
"deadlock" error will be returned.

Note that the new `backpressure-deadlock.wast` test was copied from Alex
Crichton's issue description.

Fixes #152

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
@dicej dicej added this pull request to the merge queue May 2, 2025
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 2, 2025
@lukewagner
Copy link
Copy Markdown
Contributor

Hah, I just ran into the same thing. If you'd like a second test case for the same error condition but exercised differently:

(component
  (component $C
    (core module $Memory (memory (export "mem") 1))
    (core instance $memory (instantiate $Memory))
    (core module $CM
      (import "" "mem" (memory 1))
      (import "" "waitable-set.new" (func $waitable-set.new (result i32)))

      (func (export "f") (result i32)
        (local $ws i32)
        ;; return WAIT on an empty waitable set
        (local.set $ws (call $waitable-set.new))
        (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (local.get $ws) (i32.const 4)))
      )
      (func (export "cb") (param $event_code i32) (param $index i32) (param $payload i32) (result i32)
        unreachable
      )
    )
    (canon waitable-set.new (core func $waitable-set.new))
    (core instance $cm (instantiate $CM (with "" (instance
      (export "mem" (memory $memory "mem"))
      (export "waitable-set.new" (func $waitable-set.new))
    ))))
    (func (export "f") (result u32) (canon lift
      (core func $cm "f")
      async (memory $memory "mem") (callback (func $cm "cb"))
    ))
  )

  (component $D
    (import "f" (func $f (result u32)))

    (core module $Memory (memory (export "mem") 1))
    (core instance $memory (instantiate $Memory))
    (core module $DM
      (import "" "mem" (memory 1))
      (import "" "waitable.join" (func $waitable.join (param i32 i32)))
      (import "" "waitable-set.new" (func $waitable-set.new (result i32)))
      (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
      (import "" "f" (func $f (param i32 i32) (result i32)))

      (func (export "g") (result i32)
        (local $ws i32) (local $ret i32) (local $subtaski i32)
        (local.set $ws (call $waitable-set.new))
        (local.set $ret (call $f (i32.const 0) (i32.const 0)))
        (local.set $subtaski (i32.shr_u (local.get $ret) (i32.const 4)))
        (call $waitable.join (local.get $subtaski) (local.get $ws))
        (call $waitable-set.wait (local.get $ws) (i32.const 0))
        unreachable
      )
    )
    (canon waitable.join (core func $waitable.join))
    (canon waitable-set.new (core func $waitable-set.new))
    (canon waitable-set.wait (memory $memory "mem") (core func $waitable-set.wait))
    (canon lower (func $f) async (memory $memory "mem") (core func $f'))
    (core instance $dm (instantiate $DM (with "" (instance
      (export "mem" (memory $memory "mem"))
      (export "waitable.join" (func $waitable.join))
      (export "waitable-set.new" (func $waitable-set.new))
      (export "waitable-set.wait" (func $waitable-set.wait))
      (export "f" (func $f'))
    ))))
    (func (export "f") (result u32) (canon lift (core func $dm "g")))
  )

  (instance $c (instantiate $C))
  (instance $d (instantiate $D (with "f" (func $c "f"))))
  (func (export "f") (alias export $d "f"))
)
(assert_trap (invoke "f") "deadlock detected")

@dicej
Copy link
Copy Markdown
Collaborator Author

dicej commented May 5, 2025

@lukewagner thanks for the extra test; I'll add it. As I added Alex's test, I noticed we already had another one that triggered this error, so now we have three total.

This creates a new `AsyncDeadlock` variant to the `Trap` enum, allowing
application code to downcast to `Trap` rather than do a string match to handle
the error programmatically.

Thanks to Luke Wagner for the additional test case.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
@dicej dicej enabled auto-merge May 5, 2025 16:51
@dicej dicej added this pull request to the merge queue May 5, 2025
Merged via the queue into main with commit c51ff10 May 5, 2025
44 checks passed
@dicej dicej deleted the dicej/fix-152 branch May 5, 2025 20:53
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unusual trap when deadlock expected

2 participants