Skip to content

component-model-async: Cannot reenter component during a task's yield? #12128

@alexcrichton

Description

@alexcrichton

This test case:

(component
  (component $a
    (core module $a
      (import "" "yield" (func $yield (result i32)))

      (func (export "yield-loop") (result i32)
        ;; simulate `waitable-set.poll` with a yield loop
        (loop
          call $yield
          drop
          br 0
        )
        unreachable
      )

      ;; not reached
      (func (export "callback") (param i32 i32 i32) (result i32) unreachable)

      (func (export "noop"))
    )
    (core func $yield (canon thread.yield))
    (core instance $a (instantiate $a
      (with "" (instance
        (export "yield" (func $yield))
      ))
    ))
    (func (export "yield-loop") async
      (canon lift
        (core func $a "yield-loop")
        async
        (callback (func $a "callback"))
      )
    )
    (func (export "noop") (canon lift (core func $a "noop")))
  )
  (instance $a (instantiate $a))

  (component $b
    (import "yield-loop" (func $yield-loop async))
    (import "noop" (func $noop))

    (core func $yield-loop (canon lower (func $yield-loop) async))
    (core func $noop (canon lower (func $noop)))

    (core module $b
      (import "" "yield-loop" (func $yield-loop (result i32)))
      (import "" "noop" (func $noop))

      (func (export "run")
        ;; call `yield-loop`, double-check it's in the "started" state.
        call $yield-loop
        i32.const 0xf
        i32.and
        i32.const 1
        i32.ne
        if unreachable end

        ;; now try to reenter the other component with some other function.
        call $noop
      )
    )
    (core instance $b (instantiate $b
      (with "" (instance
        (export "yield-loop" (func $yield-loop))
        (export "noop" (func $noop))
      ))
    ))
    (func (export "run") async (canon lift (core func $b "run")))
  )
  (instance $b (instantiate $b
    (with "yield-loop" (func $a "yield-loop"))
    (with "noop" (func $a "noop"))
  ))
  (export "run" (func $b "run"))
)

(assert_return (invoke "run"))

fails with:

$ WASMTIME_BACKTRACE_DETAILS=1 wasmtime wast foo.wast -W component-model-async
Error: failed to run script file 'foo.wast'

Caused by:
    0: failed directive on foo.wast:77
    1: error while executing at wasm backtrace:
           0:    0x185 - <unknown>!<wasm function 8>
           1:    0x333 - wasm-function[2]
                           at ./foo.wast:59:9
    2: wasm trap: wasm `unreachable` instruction executed

Specifically $yield-loop is, well, yielding, and then the second call to $noop I believe is dying in the fused adapter. My best guess is due to component reentrance, but I'm not 100% certain since errors from the fused adapter are pretty opaque.

Is this intended behavior? Accidental behavior?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Backlog

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions