|
236 | 236 | (func (export "run") (alias export $d "run")) |
237 | 237 | ) |
238 | 238 | (assert_return (invoke "run") (u32.const 42)) |
| 239 | + |
| 240 | +;; Test where the writer has a pending buffer that is drained by several reads |
| 241 | +;; and then the readable end is dropped (before the writer has observed its event). |
| 242 | +(component definition $Tester |
| 243 | + (core module $Memory (memory (export "mem") 1)) |
| 244 | + (core instance $memory (instantiate $Memory)) |
| 245 | + (core module $M |
| 246 | + (import "" "mem" (memory 1)) |
| 247 | + (import "" "waitable.join" (func $waitable.join (param i32 i32))) |
| 248 | + (import "" "waitable-set.new" (func $waitable-set.new (result i32))) |
| 249 | + (import "" "waitable-set.poll" (func $waitable-set.poll (param i32 i32) (result i32))) |
| 250 | + (import "" "stream.new" (func $stream.new (result i64))) |
| 251 | + (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32))) |
| 252 | + (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32))) |
| 253 | + (import "" "stream.drop-readable" (func $stream.drop-readable (param i32))) |
| 254 | + |
| 255 | + (global $ws (mut i32) (i32.const 0)) |
| 256 | + (global $rx (mut i32) (i32.const 0)) |
| 257 | + (global $tx (mut i32) (i32.const 0)) |
| 258 | + |
| 259 | + (func $start |
| 260 | + (global.set $ws (call $waitable-set.new)) |
| 261 | + ) |
| 262 | + (start $start) |
| 263 | + |
| 264 | + (func $expect-event (param $code i32) (param $index i32) (param $payload i32) |
| 265 | + (if (i32.ne (call $waitable-set.poll (global.get $ws) (i32.const 0)) (local.get $code)) |
| 266 | + (then unreachable)) |
| 267 | + (if (i32.ne (i32.load (i32.const 0)) (local.get $index)) |
| 268 | + (then unreachable)) |
| 269 | + (if (i32.ne (i32.load (i32.const 4)) (local.get $payload)) |
| 270 | + (then unreachable)) |
| 271 | + ) |
| 272 | + (func $expect-no-event |
| 273 | + (if (i32.ne (call $waitable-set.poll (global.get $ws) (i32.const 0)) |
| 274 | + (i32.const 0 (; NONE ;))) |
| 275 | + (then unreachable)) |
| 276 | + ) |
| 277 | + ;; Create a stream and block a 4-byte write on it, with the writable end in |
| 278 | + ;; the waitable set so that its completion is observable. |
| 279 | + (func $blocked-write (param $n i32) |
| 280 | + (local $ret64 i64) |
| 281 | + (local.set $ret64 (call $stream.new)) |
| 282 | + (global.set $rx (i32.wrap_i64 (local.get $ret64))) |
| 283 | + (global.set $tx (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32)))) |
| 284 | + (call $waitable.join (global.get $tx) (global.get $ws)) |
| 285 | + (i32.store (i32.const 64) (i32.const 0x04030201)) |
| 286 | + (if (i32.ne (call $stream.write (global.get $tx) (i32.const 64) (local.get $n)) |
| 287 | + (i32.const -1 (; BLOCKED ;))) |
| 288 | + (then unreachable)) |
| 289 | + ) |
| 290 | + |
| 291 | + ;; A blocked 3-byte write is drained by a 1-byte then a 2-byte read. The |
| 292 | + ;; reads see the bytes in order, and the writer gets one event carrying the |
| 293 | + ;; total progress, not one event per read. |
| 294 | + (func (export "writer-buffer-pending") (result i32) |
| 295 | + (call $blocked-write (i32.const 3)) |
| 296 | + (if (i32.ne (call $stream.read (global.get $rx) (i32.const 128) (i32.const 1)) |
| 297 | + (i32.const 0x10 (; COMPLETED=0 | (1<<4) ;))) |
| 298 | + (then unreachable)) |
| 299 | + ;; NB: the writer is not polled in between -- observing its event would |
| 300 | + ;; detach its buffer and the second read would have nothing to drain. |
| 301 | + (if (i32.ne (call $stream.read (global.get $rx) (i32.const 129) (i32.const 2)) |
| 302 | + (i32.const 0x20 (; COMPLETED=0 | (2<<4) ;))) |
| 303 | + (then unreachable)) |
| 304 | + (call $expect-event (i32.const 3 (; STREAM_WRITE ;)) (global.get $tx) |
| 305 | + (i32.const 0x30 (; COMPLETED=0 | (3<<4) ;))) |
| 306 | + (call $expect-no-event) |
| 307 | + ;; bytes 1,2,3 landed contiguously and in order |
| 308 | + (if (i32.ne (i32.load (i32.const 128)) (i32.const 0x00030201)) |
| 309 | + (then unreachable)) |
| 310 | + (i32.const 42) |
| 311 | + ) |
| 312 | + |
| 313 | + ;; If the readable end is dropped before the writer observes its event, the |
| 314 | + ;; pending COMPLETED is folded into a DROPPED carrying the same progress: |
| 315 | + ;; the writer learns both that its bytes were copied and that the stream is |
| 316 | + ;; over, in one event. |
| 317 | + (func (export "drop-folds-into-full-progress") (result i32) |
| 318 | + (call $blocked-write (i32.const 4)) |
| 319 | + (if (i32.ne (call $stream.read (global.get $rx) (i32.const 128) (i32.const 4)) |
| 320 | + (i32.const 0x40 (; COMPLETED=0 | (4<<4) ;))) |
| 321 | + (then unreachable)) |
| 322 | + (call $stream.drop-readable (global.get $rx)) |
| 323 | + (call $expect-event (i32.const 3 (; STREAM_WRITE ;)) (global.get $tx) |
| 324 | + (i32.const 0x41 (; DROPPED=1 | (4<<4) ;))) |
| 325 | + (call $expect-no-event) |
| 326 | + (i32.const 42) |
| 327 | + ) |
| 328 | + |
| 329 | + ;; Same, with the write only partially drained: the folded event reports |
| 330 | + ;; the partial progress. |
| 331 | + (func (export "drop-folds-into-partial-progress") (result i32) |
| 332 | + (call $blocked-write (i32.const 4)) |
| 333 | + (if (i32.ne (call $stream.read (global.get $rx) (i32.const 128) (i32.const 2)) |
| 334 | + (i32.const 0x20 (; COMPLETED=0 | (2<<4) ;))) |
| 335 | + (then unreachable)) |
| 336 | + (call $stream.drop-readable (global.get $rx)) |
| 337 | + (call $expect-event (i32.const 3 (; STREAM_WRITE ;)) (global.get $tx) |
| 338 | + (i32.const 0x21 (; DROPPED=1 | (2<<4) ;))) |
| 339 | + (call $expect-no-event) |
| 340 | + (i32.const 42) |
| 341 | + ) |
| 342 | + ) |
| 343 | + (type $ST (stream u8)) |
| 344 | + (canon waitable.join (core func $waitable.join)) |
| 345 | + (canon waitable-set.new (core func $waitable-set.new)) |
| 346 | + (canon waitable-set.poll (memory (core memory $memory "mem")) (core func $waitable-set.poll)) |
| 347 | + (canon stream.new $ST (core func $stream.new)) |
| 348 | + (canon stream.read $ST async (memory (core memory $memory "mem")) (core func $stream.read)) |
| 349 | + (canon stream.write $ST async (memory (core memory $memory "mem")) (core func $stream.write)) |
| 350 | + (canon stream.drop-readable $ST (core func $stream.drop-readable)) |
| 351 | + (core instance $m (instantiate $M (with "" (instance |
| 352 | + (export "mem" (memory $memory "mem")) |
| 353 | + (export "waitable.join" (func $waitable.join)) |
| 354 | + (export "waitable-set.new" (func $waitable-set.new)) |
| 355 | + (export "waitable-set.poll" (func $waitable-set.poll)) |
| 356 | + (export "stream.new" (func $stream.new)) |
| 357 | + (export "stream.read" (func $stream.read)) |
| 358 | + (export "stream.write" (func $stream.write)) |
| 359 | + (export "stream.drop-readable" (func $stream.drop-readable)) |
| 360 | + )))) |
| 361 | + (func (export "writer-buffer-pending") (result u32) (canon lift (core func $m "writer-buffer-pending"))) |
| 362 | + (func (export "drop-folds-into-full-progress") (result u32) (canon lift (core func $m "drop-folds-into-full-progress"))) |
| 363 | + (func (export "drop-folds-into-partial-progress") (result u32) (canon lift (core func $m "drop-folds-into-partial-progress"))) |
| 364 | +) |
| 365 | +(component instance $i $Tester) |
| 366 | +(assert_return (invoke "writer-buffer-pending") (u32.const 42)) |
| 367 | +(component instance $i $Tester) |
| 368 | +(assert_return (invoke "drop-folds-into-full-progress") (u32.const 42)) |
| 369 | +(component instance $i $Tester) |
| 370 | +(assert_return (invoke "drop-folds-into-partial-progress") (u32.const 42)) |
0 commit comments