Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/misc_testsuite/component-model/async/trap-if-block-and-sync.wast
2450 views
;;! component_model_async = true
;;! component_model_threading = true
;;! reference_types = true
;;! gc_types = true
;;! multi_memory = true

;; TODO: remove this in favor of the upstream version once
;; https://github.com/WebAssembly/component-model/pull/578 has been merged.

;; The $Tester component has two nested components $C and $D, where $D imports
;; and calls $C. $C contains utilities used by $D to perform all the tests.
;; Most of the tests trap, $Tester exports 1 function per test and a fresh
;; $Tester is created to run each test.
(component definition $Tester
  (component $C
    (core module $Memory (memory (export "mem") 1))
    (core instance $memory (instantiate $Memory))
    (core module $CM
      (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 "" "future.new" (func $future.new (result i64)))
      (import "" "future.read" (func $future.read (param i32 i32) (result i32)))
      (import "" "future.write" (func $future.write (param i32 i32) (result i32)))

      ;; $ws is waited on by 'blocker' and added to by 'unblocker'
      (global $ws (mut i32) (i32.const 0))
      (func $start (global.set $ws (call $waitable-set.new)))
      (start $start)

      (func (export "blocker") (result i32)
        ;; wait on $ws, which is initially empty, but will be populated with
        ;; a completed future when "unblocker" synchronously barges in.
        (local $ret i32)
        (local.set $ret (call $waitable-set.wait (global.get $ws) (i32.const 0)))
        (if (i32.ne (i32.const 4 (; FUTURE_READ ;)) (local.get $ret))
          (then unreachable))
        (if (i32.ne (i32.const 0 (; COMPLETED ;)) (i32.load (i32.const 4)))
          (then unreachable))

        (i32.const 42)
      )

      (func (export "unblocker") (result i32)
        (local $ret i32) (local $ret64 i64)
        (local $futr i32) (local $futw i32)

        ;; create read/write futures that will be used to unblock 'blocker'
        (local.set $ret64 (call $future.new))
        (local.set $futr (i32.wrap_i64 (local.get $ret64)))
        (local.set $futw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))

        ;; perform a future.read which will block, and add this future to the waitable-set
        ;; being waited on by 'blocker'
        (local.set $ret (call $future.read (local.get $futr) (i32.const 0xdeadbeef)))
        (if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret))
          (then unreachable))
        (call $waitable.join (local.get $futr) (global.get $ws))

        ;; perform a future.write which will rendezvous with the write and complete
        (local.set $ret (call $future.write (local.get $futw) (i32.const 0xdeadbeef)))
        (if (i32.ne (i32.const 0 (; COMPLETED ;)) (local.get $ret))
          (then unreachable))

        (i32.const 43)
      )

      (func (export "sync-async-func")
        unreachable
      )
      (func (export "async-async-func") (result i32)
        unreachable
      )
      (func (export "async-async-func-cb") (param i32 i32 i32) (result i32)
        unreachable
      )
    )
    (type $FT (future))
    (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 future.new $FT (core func $future.new))
    (canon future.read $FT async (core func $future.read))
    (canon future.write $FT async (core func $future.write))
    (core instance $cm (instantiate $CM (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 "future.new" (func $future.new))
      (export "future.read" (func $future.read))
      (export "future.write" (func $future.write))
    ))))
    (func (export "blocker") async (result u32) (canon lift (core func $cm "blocker")))
    (func (export "unblocker") (result u32) (canon lift (core func $cm "unblocker")))
    (func (export "sync-async-func") async (canon lift (core func $cm "sync-async-func")))
    (func (export "async-async-func") async (canon lift (core func $cm "async-async-func") async (callback (func $cm "async-async-func-cb"))))
  )
  (component $D
    (import "c" (instance $c
      (export "blocker" (func async (result u32)))
      (export "unblocker" (func (result u32)))
      (export "sync-async-func" (func async))
      (export "async-async-func" (func async))
    ))

    (core module $Memory (memory (export "mem") 1))
    (core instance $memory (instantiate $Memory))
    (core module $Core
      (import "" "mem" (memory 1))
      (import "" "task.return" (func $task.return (param i32)))
      (import "" "subtask.cancel" (func $subtask.cancel (param i32) (result i32)))
      (import "" "thread.yield" (func $thread.yield (result i32)))
      (import "" "thread.suspend" (func $thread.suspend (result i32)))
      (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 "" "waitable-set.poll" (func $waitable-set.poll (param i32 i32) (result i32)))
      (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
      (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
      (import "" "future.read" (func $future.read (param i32 i32) (result i32)))
      (import "" "future.write" (func $future.write (param i32 i32) (result i32)))
      (import "" "stream.cancel-read" (func $stream.cancel-read (param i32) (result i32)))
      (import "" "stream.cancel-write" (func $stream.cancel-write (param i32) (result i32)))
      (import "" "future.cancel-read" (func $future.cancel-read (param i32) (result i32)))
      (import "" "future.cancel-write" (func $future.cancel-write (param i32) (result i32)))
      (import "" "blocker" (func $blocker (param i32) (result i32)))
      (import "" "unblocker" (func $unblocker (result i32)))
      (import "" "await-sync-async-func" (func $await-sync-async-func))
      (import "" "await-async-async-func" (func $await-async-async-func))

      (func (export "sync-barges-in") (result i32)
        (local $ret i32) (local $retp1 i32) (local $retp2 i32)
        (local $subtask i32) (local $ws i32) (local $event_code i32)

        (local.set $retp1 (i32.const 8))
        (local.set $retp2 (i32.const 12))

        ;; call $blocker which will block during a synchronous function.
        (local.set $ret (call $blocker (local.get $retp1)))
        (if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf)))
          (then unreachable))
        (local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4)))

        ;; normally calling another function would hit backpressure until
        ;; $blocker was done, but calling the sync-typed function $unblocker
        ;; barges in synchronously.
        (local.set $ret (call $unblocker))
        (if (i32.ne (local.get $ret) (i32.const 43))
          (then unreachable))

        ;; now wait to confirm that $subtask was actually unblocked
        (local.set $ws (call $waitable-set.new))
        (call $waitable.join (local.get $subtask) (local.get $ws))
        (local.set $event_code (call $waitable-set.wait (local.get $ws) (local.get $retp2)))
        (if (i32.ne (i32.const 1 (; SUBTASK ;)) (local.get $event_code))
          (then unreachable))
        (if (i32.ne (local.get $subtask) (i32.load (local.get $retp2)))
          (then unreachable))
        (if (i32.ne (i32.const 2 (; RETURNED=2 ;)) (i32.load offset=4 (local.get $retp2)))
          (then unreachable))
        (if (i32.ne (i32.const 42) (i32.load (local.get $retp1)))
          (then unreachable))

        (i32.const 44)
      )

      (func (export "unreachable-cb") (param i32 i32 i32) (result i32)
        unreachable
      )
      (func (export "return-42-cb") (param i32 i32 i32) (result i32)
        (call $task.return (i32.const 42))
        (i32.const 0 (; EXIT ;))
      )

      (func (export "trap-if-sync-call-async1")
        (call $await-sync-async-func)
      )
      (func (export "trap-if-sync-call-async2")
        (call $await-async-async-func)
      )
      (func (export "trap-if-suspend")
        (call $thread.suspend)
        unreachable
      )
      (func (export "trap-if-wait")
        (call $waitable-set.wait (call $waitable-set.new) (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-wait-cb") (result i32)
        (i32.or
          (i32.const 2 (; WAIT ;))
          (i32.shl (call $waitable-set.new) (i32.const 4)))
      )
      (func (export "trap-if-poll")
        (call $waitable-set.poll (call $waitable-set.new) (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-poll-cb") (result i32)
        (i32.or
          (i32.const 3 (; POLL ;))
          (i32.shl (call $waitable-set.new) (i32.const 4)))
      )
      (func (export "yield-is-fine") (result i32)
        (drop (call $thread.yield))
        (i32.const 42)
      )
      (func (export "yield-is-fine-cb") (result i32)
        (i32.or
          (i32.const 1 (; YIELD ;))
          (i32.shl (i32.const 0xdead) (i32.const 4)))
      )
      (func (export "trap-if-sync-cancel")
        (call $subtask.cancel (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-sync-stream-read")
        (call $stream.read (i32.const 0xdead) (i32.const 0xbeef) (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-sync-stream-write")
        (call $stream.write (i32.const 0xdead) (i32.const 0xbeef) (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-sync-future-read")
        (call $future.read (i32.const 0xdead) (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-sync-future-write")
        (call $future.write (i32.const 0xdead) (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-sync-stream-cancel-read")
        (call $stream.cancel-read (i32.const 0xdead))
        unreachable
      )
      (func (export "trap-if-sync-stream-cancel-write")
        (call $stream.cancel-write (i32.const 0xdead))
        unreachable
      )
      (func (export "trap-if-sync-future-cancel-read")
        (call $future.cancel-read (i32.const 0xdead) (i32.const 0xdeadbeef))
        unreachable
      )
      (func (export "trap-if-sync-future-cancel-write")
        (call $future.cancel-write (i32.const 0xdead) (i32.const 0xdeadbeef))
        unreachable
      )
    )
    (type $FT (future u8))
    (type $ST (stream u8))
    (canon task.return (result u32) (core func $task.return))
    (canon subtask.cancel (core func $subtask.cancel))
    (canon thread.yield (core func $thread.yield))
    (canon thread.suspend (core func $thread.suspend))
    (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 waitable-set.poll (memory $memory "mem") (core func $waitable-set.poll))
    (canon stream.read $ST (memory $memory "mem") (core func $stream.read))
    (canon stream.write $ST (memory $memory "mem") (core func $stream.write))
    (canon future.read $FT (memory $memory "mem") (core func $future.read))
    (canon future.write $FT (memory $memory "mem") (core func $future.write))
    (canon stream.cancel-read $ST (core func $stream.cancel-read))
    (canon stream.cancel-write $ST (core func $stream.cancel-write))
    (canon future.cancel-read $FT (core func $future.cancel-read))
    (canon future.cancel-write $FT (core func $future.cancel-write))
    (canon lower (func $c "blocker") (memory $memory "mem") async (core func $blocker'))
    (canon lower (func $c "unblocker") (core func $unblocker'))
    (canon lower (func $c "sync-async-func") (core func $await-sync-async-func'))
    (canon lower (func $c "async-async-func") (core func $await-async-async-func'))
    (core instance $core (instantiate $Core (with "" (instance
      (export "mem" (memory $memory "mem"))
      (export "task.return" (func $task.return))
      (export "subtask.cancel" (func $subtask.cancel))
      (export "thread.yield" (func $thread.yield))
      (export "thread.suspend" (func $thread.suspend))
      (export "waitable.join" (func $waitable.join))
      (export "waitable-set.new" (func $waitable-set.new))
      (export "waitable-set.wait" (func $waitable-set.wait))
      (export "waitable-set.poll" (func $waitable-set.poll))
      (export "stream.read" (func $stream.read))
      (export "stream.write" (func $stream.write))
      (export "future.read" (func $future.read))
      (export "future.write" (func $future.write))
      (export "stream.cancel-read" (func $stream.cancel-read))
      (export "stream.cancel-write" (func $stream.cancel-write))
      (export "future.cancel-read" (func $future.cancel-read))
      (export "future.cancel-write" (func $future.cancel-write))
      (export "blocker" (func $blocker'))
      (export "unblocker" (func $unblocker'))
      (export "await-sync-async-func" (func $await-sync-async-func'))
      (export "await-async-async-func" (func $await-async-async-func'))
    ))))
    (func (export "sync-barges-in") async (result u32) (canon lift (core func $core "sync-barges-in")))
    (func (export "trap-if-suspend") (canon lift (core func $core "trap-if-suspend")))
    (func (export "trap-if-wait") (canon lift (core func $core "trap-if-wait")))
    (func (export "trap-if-wait-cb") (canon lift (core func $core "trap-if-wait-cb") async (callback (func $core "unreachable-cb"))))
    (func (export "trap-if-poll") (canon lift (core func $core "trap-if-poll")))
    (func (export "trap-if-poll-cb") (canon lift (core func $core "trap-if-poll-cb") async (callback (func $core "unreachable-cb"))))
    (func (export "yield-is-fine") (result u32) (canon lift (core func $core "yield-is-fine")))
    (func (export "yield-is-fine-cb") (result u32) (canon lift (core func $core "yield-is-fine-cb") async (callback (func $core "return-42-cb"))))
    (func (export "trap-if-sync-call-async1") (canon lift (core func $core "trap-if-sync-call-async1")))
    (func (export "trap-if-sync-call-async2") (canon lift (core func $core "trap-if-sync-call-async2")))
    (func (export "trap-if-sync-cancel") (canon lift (core func $core "trap-if-sync-cancel")))
    (func (export "trap-if-sync-stream-read") (canon lift (core func $core "trap-if-sync-stream-read")))
    (func (export "trap-if-sync-stream-write") (canon lift (core func $core "trap-if-sync-stream-write")))
    (func (export "trap-if-sync-future-read") (canon lift (core func $core "trap-if-sync-future-read")))
    (func (export "trap-if-sync-future-write") (canon lift (core func $core "trap-if-sync-future-write")))
    (func (export "trap-if-sync-stream-cancel-read") (canon lift (core func $core "trap-if-sync-stream-cancel-read")))
    (func (export "trap-if-sync-stream-cancel-write") (canon lift (core func $core "trap-if-sync-stream-cancel-write")))
    (func (export "trap-if-sync-future-cancel-read") (canon lift (core func $core "trap-if-sync-future-cancel-read")))
    (func (export "trap-if-sync-future-cancel-write") (canon lift (core func $core "trap-if-sync-future-cancel-write")))
  )
  (instance $c (instantiate $C))
  (instance $d (instantiate $D (with "c" (instance $c))))
  (func (export "sync-barges-in") (alias export $d "sync-barges-in"))
  (func (export "trap-if-sync-call-async1") (alias export $d "trap-if-sync-call-async1"))
  (func (export "trap-if-sync-call-async2") (alias export $d "trap-if-sync-call-async2"))
  (func (export "trap-if-suspend") (alias export $d "trap-if-suspend"))
  (func (export "trap-if-wait") (alias export $d "trap-if-wait"))
  (func (export "trap-if-wait-cb") (alias export $d "trap-if-wait-cb"))
  (func (export "trap-if-poll") (alias export $d "trap-if-poll"))
  (func (export "trap-if-poll-cb") (alias export $d "trap-if-poll-cb"))
  (func (export "yield-is-fine") (alias export $d "yield-is-fine"))
  (func (export "yield-is-fine-cb") (alias export $d "yield-is-fine-cb"))
  (func (export "trap-if-sync-cancel") (alias export $d "trap-if-sync-cancel"))
  (func (export "trap-if-sync-stream-read") (alias export $d "trap-if-sync-stream-read"))
  (func (export "trap-if-sync-stream-write") (alias export $d "trap-if-sync-stream-write"))
  (func (export "trap-if-sync-future-read") (alias export $d "trap-if-sync-future-read"))
  (func (export "trap-if-sync-future-write") (alias export $d "trap-if-sync-future-write"))
  (func (export "trap-if-sync-stream-cancel-read") (alias export $d "trap-if-sync-stream-cancel-read"))
  (func (export "trap-if-sync-stream-cancel-write") (alias export $d "trap-if-sync-stream-cancel-write"))
  (func (export "trap-if-sync-future-cancel-read") (alias export $d "trap-if-sync-future-cancel-read"))
  (func (export "trap-if-sync-future-cancel-write") (alias export $d "trap-if-sync-future-cancel-write"))
)

(component instance $i $Tester)
(assert_return (invoke "sync-barges-in") (u32.const 44))

(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-call-async1") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-call-async2") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-suspend") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-wait") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-wait-cb") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-poll") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-poll-cb") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_return (invoke "yield-is-fine") (u32.const 42))
(component instance $i $Tester)
(assert_return (invoke "yield-is-fine-cb") (u32.const 42))
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-cancel") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-stream-read") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-stream-write") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-future-read") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-future-write") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-stream-cancel-read") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-stream-cancel-write") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-future-cancel-read") "cannot block a synchronous task before returning")
(component instance $i $Tester)
(assert_trap (invoke "trap-if-sync-future-cancel-write") "cannot block a synchronous task before returning")