Stream: git-wasmtime

Topic: wasmtime / issue #13661 Possible to drop a future writabl...


view this post on Zulip Wasmtime GitHub notifications bot (Jun 16 2026 at 00:22):

alexcrichton opened issue #13661:

This test:

(component
  (core module $libc (memory (export "m") 1))
  (core instance $libc (instantiate $libc))

  (type $f (future u8))
  (core func $future.new (canon future.new $f))
  (core func $future.drop-readable (canon future.drop-readable $f))
  (core func $future.drop-writable (canon future.drop-writable $f))

  (core module $m
    (import "" "m" (memory 1))
    (import "" "future.new" (func $future.new (result i64)))
    (import "" "future.drop-readable" (func $future.drop-readable (param i32)))
    (import "" "future.drop-writable" (func $future.drop-writable (param i32)))

    (func (export "run")
      (local $tmp i64) (local $r i32) (local $w i32)
      (local.set $tmp (call $future.new))
      (local.set $r (i32.wrap_i64 (local.get $tmp)))
      (local.set $w (i32.wrap_i64 (i64.shr_u (local.get $tmp) (i64.const 32))))
      (call $future.drop-readable (local.get $r))
      (call $future.drop-writable (local.get $w))
    )
  )
  (core instance $i (instantiate $m
    (with "" (instance
      (export "m" (memory $libc "m"))
      (export "future.new" (func $future.new))
      (export "future.drop-readable" (func $future.drop-readable))
      (export "future.drop-writable" (func $future.drop-writable))
    ))
  ))
  (func (export "run") async (canon lift (core func $i "run")))
)
(assert_trap (invoke "run") "something here")

creates a future, closed the read end, then closes the write end. I think this should be trap, but currently Wasmtime executes this.

cc @dicej - would you be able to take a look at this?

view this post on Zulip Wasmtime GitHub notifications bot (Jun 16 2026 at 00:22):

alexcrichton added the wasm-proposal:component-model-async label to Issue #13661.

view this post on Zulip Wasmtime GitHub notifications bot (Jun 18 2026 at 19:28):

cschanhniem commented on issue #13661:

This trap path is specified in the component model async ABI: canon future.new returns an i64 packing both handles ((i64.or (i64.shl write-i32 32) read-i32)), and the writable end carries a "must write before close" obligation. The canonical ABI for future.drop-writable should trap when the future state is created-but-unwritten — that's distinct from the existing trap-if-sync-and-waitable-set checks in the sync lowering paths.

The test you linked from CM/#647 covers the sync path, but the async lowering in Wasmtime would hit a different code path in crates/wasmtime/src/runtime/vm/component/futures.rs — the Drop impl or close_writable method needs an additional state check on the writable end. The relevant state machine transitions are: Created → Written → ReadableClosed → WritableClosed. Dropping the writable end in Created should emit the trap, not silently close.

view this post on Zulip Wasmtime GitHub notifications bot (Jun 18 2026 at 19:42):

dicej assigned dicej to issue #13661.

view this post on Zulip Wasmtime GitHub notifications bot (Jun 19 2026 at 01:23):

alexcrichton closed issue #13661:

This test:

(component
  (core module $libc (memory (export "m") 1))
  (core instance $libc (instantiate $libc))

  (type $f (future u8))
  (core func $future.new (canon future.new $f))
  (core func $future.drop-readable (canon future.drop-readable $f))
  (core func $future.drop-writable (canon future.drop-writable $f))

  (core module $m
    (import "" "m" (memory 1))
    (import "" "future.new" (func $future.new (result i64)))
    (import "" "future.drop-readable" (func $future.drop-readable (param i32)))
    (import "" "future.drop-writable" (func $future.drop-writable (param i32)))

    (func (export "run")
      (local $tmp i64) (local $r i32) (local $w i32)
      (local.set $tmp (call $future.new))
      (local.set $r (i32.wrap_i64 (local.get $tmp)))
      (local.set $w (i32.wrap_i64 (i64.shr_u (local.get $tmp) (i64.const 32))))
      (call $future.drop-readable (local.get $r))
      (call $future.drop-writable (local.get $w))
    )
  )
  (core instance $i (instantiate $m
    (with "" (instance
      (export "m" (memory $libc "m"))
      (export "future.new" (func $future.new))
      (export "future.drop-readable" (func $future.drop-readable))
      (export "future.drop-writable" (func $future.drop-writable))
    ))
  ))
  (func (export "run") async (canon lift (core func $i "run")))
)
(assert_trap (invoke "run") "something here")

creates a future, closed the read end, then closes the write end. I think this should be trap, but currently Wasmtime executes this.

cc @dicej - would you be able to take a look at this?


Last updated: Jul 29 2026 at 05:03 UTC