Skip to content

Commit 552303c

Browse files
authored
Fix intra-component stream/futures copies with multi-memory (#12966)
This commit fixes a mistake from #12872 where an intra-component copy of a stream/future payload incorrectly use a `copy_within` when different memories were in use. Previously instance identity was used to test whether `copy_within` can be used but the correct check is to use memory identity. This handles the case where a single component instance has multiple memories in use, for example.
1 parent 3c714e3 commit 552303c

2 files changed

Lines changed: 190 additions & 4 deletions

File tree

crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3295,7 +3295,9 @@ impl Instance {
32953295

32963296
assert_eq!(read_length_in_bytes, write_length_in_bytes);
32973297

3298-
if write_caller_instance == read_caller_instance {
3298+
if self.options_memory(store_opaque, read_options).as_ptr()
3299+
== self.options_memory(store_opaque, write_options).as_ptr()
3300+
{
32993301
let memory = self.options_memory_mut(store_opaque, read_options);
33003302
memory.copy_within(
33013303
write_address..write_address + write_length_in_bytes,
@@ -3313,9 +3315,9 @@ impl Instance {
33133315
// above to be valid pointers as they're derived from
33143316
// slices that have the desired length with the desired
33153317
// read/write permission. The `unsafe` bit here is that
3316-
// the memories are disjoint (present in different
3317-
// instances) and there's no easy way to borrow both
3318-
// simultaneously from the store. Different instances
3318+
// the memories are disjoint (different base pointers)
3319+
// and there's no easy way to borrow both
3320+
// simultaneously from the store. Different memories
33193321
// are guaranteed to be disjoint, however, so the
33203322
// `unsafe` here should be ok.
33213323
unsafe {

tests/misc_testsuite/component-model/async/intra-streams.wast

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
;;! component_model_async = true
22
;;! component_model_async_builtins = true
3+
;;! multi_memory = true
34

45
(component
56
(core module $libc
@@ -53,3 +54,186 @@
5354
)
5455

5556
(assert_trap (invoke "run") "cannot read from and write to intra-component future/stream with non-numeric payload")
57+
58+
;; intra-component u64 works
59+
(component
60+
(core module $libc
61+
(memory (export "m") 1)
62+
)
63+
(core instance $libc (instantiate $libc))
64+
65+
(type $s (stream u64))
66+
(core func $stream.new (canon stream.new $s))
67+
(core func $stream.read (canon stream.read $s async (memory $libc "m")))
68+
(core func $stream.write (canon stream.write $s async (memory $libc "m")))
69+
70+
(core module $m
71+
(import "" "m" (memory 1))
72+
(import "" "stream.new" (func $stream.new (result i64)))
73+
(import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
74+
(import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
75+
76+
(func (export "run")
77+
(local $tmp i64)
78+
(local $r i32)
79+
(local $w i32)
80+
(local.set $tmp (call $stream.new))
81+
82+
(local.set $r (i32.wrap_i64 (local.get $tmp)))
83+
(local.set $w (i32.wrap_i64 (i64.shr_u (local.get $tmp) (i64.const 32))))
84+
85+
(call $stream.read (local.get $r) (i32.const 0) (i32.const 1))
86+
i32.const -1 ;; BLOCKED
87+
i32.ne
88+
if unreachable end
89+
90+
(i64.store (i32.const 8) (i64.const 100))
91+
(call $stream.write (local.get $w) (i32.const 8) (i32.const 1))
92+
i32.const 0x10 ;; (1 << 4) | COMPLETED
93+
i32.ne
94+
if unreachable end
95+
96+
(i64.load (i32.const 0))
97+
i64.const 100
98+
i64.ne
99+
if unreachable end
100+
)
101+
)
102+
103+
(core instance $i (instantiate $m
104+
(with "" (instance
105+
(export "m" (memory $libc "m"))
106+
(export "stream.new" (func $stream.new))
107+
(export "stream.read" (func $stream.read))
108+
(export "stream.write" (func $stream.write))
109+
))
110+
))
111+
112+
(func (export "run") (canon lift (core func $i "run")))
113+
)
114+
115+
(assert_return (invoke "run"))
116+
117+
;; intra-component u64 works, even across different linear memories.
118+
(component
119+
(core module $libc
120+
(memory (export "m1") 1)
121+
(memory (export "m2") 2)
122+
)
123+
(core instance $libc (instantiate $libc))
124+
125+
(type $s (stream u64))
126+
(core func $stream.new (canon stream.new $s))
127+
(core func $stream.read (canon stream.read $s async (memory $libc "m1")))
128+
(core func $stream.write (canon stream.write $s async (memory $libc "m2")))
129+
130+
(core module $m
131+
(import "libc" "m1" (memory $m1 1))
132+
(import "libc" "m2" (memory $m2 2))
133+
(import "" "stream.new" (func $stream.new (result i64)))
134+
(import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
135+
(import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
136+
137+
(func (export "run")
138+
(local $tmp i64)
139+
(local $r i32)
140+
(local $w i32)
141+
(local.set $tmp (call $stream.new))
142+
143+
(local.set $r (i32.wrap_i64 (local.get $tmp)))
144+
(local.set $w (i32.wrap_i64 (i64.shr_u (local.get $tmp) (i64.const 32))))
145+
146+
(call $stream.read (local.get $r) (i32.const 0) (i32.const 1))
147+
i32.const -1 ;; BLOCKED
148+
i32.ne
149+
if unreachable end
150+
151+
(i64.store $m2 (i32.const 65536) (i64.const 100))
152+
(call $stream.write (local.get $w) (i32.const 65536) (i32.const 1))
153+
i32.const 0x10 ;; (1 << 4) | COMPLETED
154+
i32.ne
155+
if unreachable end
156+
157+
(i64.load $m1 (i32.const 0))
158+
i64.const 100
159+
i64.ne
160+
if unreachable end
161+
)
162+
)
163+
164+
(core instance $i (instantiate $m
165+
(with "libc" (instance $libc))
166+
(with "" (instance
167+
(export "stream.new" (func $stream.new))
168+
(export "stream.read" (func $stream.read))
169+
(export "stream.write" (func $stream.write))
170+
))
171+
))
172+
173+
(func (export "run") (canon lift (core func $i "run")))
174+
)
175+
176+
(assert_return (invoke "run"))
177+
178+
;; intrinsics should pick the correct memory
179+
(component
180+
(core module $libc
181+
(memory (export "m1") 1)
182+
(memory (export "m2") 2)
183+
)
184+
(core instance $libc (instantiate $libc))
185+
186+
(type $s (stream u64))
187+
(core func $stream.new (canon stream.new $s))
188+
(core func $stream.read (canon stream.read $s async (memory $libc "m1")))
189+
(core func $stream.write (canon stream.write $s async (memory $libc "m2")))
190+
191+
(core module $m
192+
(import "libc" "m1" (memory $m1 1))
193+
(import "libc" "m2" (memory $m2 2))
194+
(import "" "stream.new" (func $stream.new (result i64)))
195+
(import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
196+
(import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
197+
198+
(func (export "run")
199+
(local $tmp i64)
200+
(local $r i32)
201+
(local $w i32)
202+
(local.set $tmp (call $stream.new))
203+
204+
(local.set $r (i32.wrap_i64 (local.get $tmp)))
205+
(local.set $w (i32.wrap_i64 (i64.shr_u (local.get $tmp) (i64.const 32))))
206+
207+
(call $stream.read (local.get $r) (i32.const 0) (i32.const 1))
208+
i32.const -1 ;; BLOCKED
209+
i32.ne
210+
if unreachable end
211+
212+
(i64.store $m1 (i32.const 8) (i64.const 101))
213+
(i64.store $m2 (i32.const 8) (i64.const 102))
214+
215+
(call $stream.write (local.get $w) (i32.const 8) (i32.const 1))
216+
i32.const 0x10 ;; (1 << 4) | COMPLETED
217+
i32.ne
218+
if unreachable end
219+
220+
(i64.load $m1 (i32.const 0))
221+
i64.const 102
222+
i64.ne
223+
if unreachable end
224+
)
225+
)
226+
227+
(core instance $i (instantiate $m
228+
(with "libc" (instance $libc))
229+
(with "" (instance
230+
(export "stream.new" (func $stream.new))
231+
(export "stream.read" (func $stream.read))
232+
(export "stream.write" (func $stream.write))
233+
))
234+
))
235+
236+
(func (export "run") (canon lift (core func $i "run")))
237+
)
238+
239+
(assert_return (invoke "run"))

0 commit comments

Comments
 (0)