Skip to content

Commit 11b04d7

Browse files
authored
enable stream/future payload lift/lower for non-Wasm platforms (#1120)
* enable stream/future payload lift/lower for non-Wasm platforms Previously, we generated no code for non-Wasm platforms; which meant our codegen tests weren't really testing much as far as streams and futures go. Now that the tests actually do something, they uncovered a few issues which I've fixed: - Invalid code generation when using `duplicate_if_necessary: true` - Invalid code generation for stream or futures whose payloads contain one or more a streams or futures For the latter, I mimicked what we do for resources: use interior mutability to provide `take_handle` methods for `StreamReader` and `FutureReader`. Signed-off-by: Joel Dice <[email protected]> * avoid lowering same values more than once in `StreamVtable::write` Previously, we would optimistically lower all the values in the input array and then re-lower the subset which wasn't accepted the first time. Aside from being inefficient, that was also incorrect since re-lowering would fail in the cases of any resource handles, futures, or streams in the payload since we would have already taken the handles using `take_handle`. Signed-off-by: Joel Dice <[email protected]> --------- Signed-off-by: Joel Dice <[email protected]>
1 parent 51e1ef5 commit 11b04d7

File tree

4 files changed

+198
-162
lines changed

4 files changed

+198
-162
lines changed

crates/guest-rust/rt/src/async_support/future_support.rs

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use {
1111
collections::hash_map::Entry,
1212
fmt,
1313
future::{Future, IntoFuture},
14-
mem::ManuallyDrop,
1514
pin::Pin,
15+
sync::atomic::{AtomicU32, Ordering::Relaxed},
1616
task::{Context, Poll},
1717
},
1818
};
@@ -199,7 +199,8 @@ impl<T> CancelableRead<T> {
199199

200200
fn cancel_mut(&mut self) -> FutureReader<T> {
201201
let reader = self.reader.take().unwrap();
202-
super::with_entry(reader.handle, |entry| match entry {
202+
let handle = reader.handle.load(Relaxed);
203+
super::with_entry(handle, |entry| match entry {
203204
Entry::Vacant(_) => unreachable!(),
204205
Entry::Occupied(mut entry) => match entry.get() {
205206
Handle::LocalOpen
@@ -209,7 +210,7 @@ impl<T> CancelableRead<T> {
209210
Handle::LocalWaiting(_) => {
210211
entry.insert(Handle::LocalOpen);
211212
}
212-
Handle::Read => (reader.vtable.cancel_read)(reader.handle),
213+
Handle::Read => (reader.vtable.cancel_read)(handle),
213214
},
214215
});
215216
reader
@@ -226,7 +227,7 @@ impl<T> Drop for CancelableRead<T> {
226227

227228
/// Represents the readable end of a Component Model `future`.
228229
pub struct FutureReader<T: 'static> {
229-
handle: u32,
230+
handle: AtomicU32,
230231
vtable: &'static FutureVtable<T>,
231232
}
232233

@@ -241,7 +242,10 @@ impl<T> fmt::Debug for FutureReader<T> {
241242
impl<T> FutureReader<T> {
242243
#[doc(hidden)]
243244
pub fn new(handle: u32, vtable: &'static FutureVtable<T>) -> Self {
244-
Self { handle, vtable }
245+
Self {
246+
handle: AtomicU32::new(handle),
247+
vtable,
248+
}
245249
}
246250

247251
#[doc(hidden)]
@@ -264,12 +268,16 @@ impl<T> FutureReader<T> {
264268
},
265269
});
266270

267-
Self { handle, vtable }
271+
Self {
272+
handle: AtomicU32::new(handle),
273+
vtable,
274+
}
268275
}
269276

270277
#[doc(hidden)]
271-
pub fn into_handle(self) -> u32 {
272-
super::with_entry(self.handle, |entry| match entry {
278+
pub fn take_handle(&self) -> u32 {
279+
let handle = self.handle.swap(u32::MAX, Relaxed);
280+
super::with_entry(handle, |entry| match entry {
273281
Entry::Vacant(_) => unreachable!(),
274282
Entry::Occupied(mut entry) => match entry.get() {
275283
Handle::LocalOpen => {
@@ -282,7 +290,7 @@ impl<T> FutureReader<T> {
282290
},
283291
});
284292

285-
ManuallyDrop::new(self).handle
293+
handle
286294
}
287295
}
288296

@@ -294,7 +302,7 @@ impl<T> IntoFuture for FutureReader<T> {
294302
/// written to the writable end of this `future` (yielding a `Some` result)
295303
/// or when the writable end is dropped (yielding a `None` result).
296304
fn into_future(self) -> Self::IntoFuture {
297-
let handle = self.handle;
305+
let handle = self.handle.load(Relaxed);
298306
let vtable = self.vtable;
299307
CancelableRead {
300308
reader: Some(self),
@@ -325,24 +333,30 @@ impl<T> IntoFuture for FutureReader<T> {
325333

326334
impl<T> Drop for FutureReader<T> {
327335
fn drop(&mut self) {
328-
super::with_entry(self.handle, |entry| match entry {
329-
Entry::Vacant(_) => unreachable!(),
330-
Entry::Occupied(mut entry) => match entry.get_mut() {
331-
Handle::LocalReady(..) => {
332-
let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed) else {
333-
unreachable!()
334-
};
335-
waker.wake();
336-
}
337-
Handle::LocalOpen | Handle::LocalWaiting(_) => {
338-
entry.insert(Handle::LocalClosed);
339-
}
340-
Handle::Read | Handle::LocalClosed => {
341-
entry.remove();
342-
(self.vtable.close_readable)(self.handle);
343-
}
344-
Handle::Write => unreachable!(),
345-
},
346-
});
336+
match self.handle.load(Relaxed) {
337+
u32::MAX => {}
338+
handle => {
339+
super::with_entry(handle, |entry| match entry {
340+
Entry::Vacant(_) => unreachable!(),
341+
Entry::Occupied(mut entry) => match entry.get_mut() {
342+
Handle::LocalReady(..) => {
343+
let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed)
344+
else {
345+
unreachable!()
346+
};
347+
waker.wake();
348+
}
349+
Handle::LocalOpen | Handle::LocalWaiting(_) => {
350+
entry.insert(Handle::LocalClosed);
351+
}
352+
Handle::Read | Handle::LocalClosed => {
353+
entry.remove();
354+
(self.vtable.close_readable)(handle);
355+
}
356+
Handle::Write => unreachable!(),
357+
},
358+
});
359+
}
360+
}
347361
}
348362
}

crates/guest-rust/rt/src/async_support/stream_support.rs

Lines changed: 94 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ use {
1515
fmt,
1616
future::Future,
1717
iter,
18-
mem::{self, ManuallyDrop, MaybeUninit},
18+
mem::{self, MaybeUninit},
1919
pin::Pin,
20+
sync::atomic::{AtomicU32, Ordering::Relaxed},
2021
task::{Context, Poll},
2122
vec::Vec,
2223
},
@@ -28,7 +29,7 @@ fn ceiling(x: usize, y: usize) -> usize {
2829

2930
#[doc(hidden)]
3031
pub struct StreamVtable<T> {
31-
pub write: fn(future: u32, values: &[T]) -> Pin<Box<dyn Future<Output = Option<usize>> + '_>>,
32+
pub write: fn(future: u32, values: &[T]) -> Pin<Box<dyn Future<Output = usize> + '_>>,
3233
pub read: fn(
3334
future: u32,
3435
values: &mut [MaybeUninit<T>],
@@ -173,14 +174,7 @@ impl<T> Sink<Vec<T>> for StreamWriter<T> {
173174
vtable,
174175
};
175176
self.get_mut().future = Some(Box::pin(async move {
176-
let mut offset = 0;
177-
while offset < item.len() {
178-
if let Some(count) = (vtable.write)(handle, &item[offset..]).await {
179-
offset += count;
180-
} else {
181-
break;
182-
}
183-
}
177+
(vtable.write)(handle, &item).await;
184178
cancel_on_drop.handle = None;
185179
drop(cancel_on_drop);
186180
}));
@@ -246,7 +240,7 @@ impl<T> Drop for CancelReadOnDrop<T> {
246240

247241
/// Represents the readable end of a Component Model `stream`.
248242
pub struct StreamReader<T: 'static> {
249-
handle: u32,
243+
handle: AtomicU32,
250244
future: Option<Pin<Box<dyn Future<Output = Option<Vec<T>>> + 'static>>>,
251245
vtable: &'static StreamVtable<T>,
252246
}
@@ -273,7 +267,7 @@ impl<T> StreamReader<T> {
273267
#[doc(hidden)]
274268
pub fn new(handle: u32, vtable: &'static StreamVtable<T>) -> Self {
275269
Self {
276-
handle,
270+
handle: AtomicU32::new(handle),
277271
future: None,
278272
vtable,
279273
}
@@ -300,15 +294,16 @@ impl<T> StreamReader<T> {
300294
});
301295

302296
Self {
303-
handle,
297+
handle: AtomicU32::new(handle),
304298
future: None,
305299
vtable,
306300
}
307301
}
308302

309303
#[doc(hidden)]
310-
pub fn into_handle(self) -> u32 {
311-
super::with_entry(self.handle, |entry| match entry {
304+
pub fn take_handle(&self) -> u32 {
305+
let handle = self.handle.swap(u32::MAX, Relaxed);
306+
super::with_entry(handle, |entry| match entry {
312307
Entry::Vacant(_) => unreachable!(),
313308
Entry::Occupied(mut entry) => match entry.get() {
314309
Handle::LocalOpen => {
@@ -321,7 +316,7 @@ impl<T> StreamReader<T> {
321316
},
322317
});
323318

324-
ManuallyDrop::new(self).handle
319+
handle
325320
}
326321
}
327322

@@ -332,60 +327,65 @@ impl<T> Stream for StreamReader<T> {
332327
let me = self.get_mut();
333328

334329
if me.future.is_none() {
335-
me.future = Some(super::with_entry(me.handle, |entry| match entry {
336-
Entry::Vacant(_) => unreachable!(),
337-
Entry::Occupied(mut entry) => match entry.get() {
338-
Handle::Write | Handle::LocalWaiting(_) => unreachable!(),
339-
Handle::Read => {
340-
let handle = me.handle;
341-
let vtable = me.vtable;
342-
let mut cancel_on_drop = CancelReadOnDrop::<T> {
343-
handle: Some(handle),
344-
vtable,
345-
};
346-
Box::pin(async move {
347-
let mut buffer = iter::repeat_with(MaybeUninit::uninit)
348-
.take(ceiling(64 * 1024, mem::size_of::<T>()))
349-
.collect::<Vec<_>>();
350-
351-
let result =
352-
if let Some(count) = (vtable.read)(handle, &mut buffer).await {
353-
buffer.truncate(count);
354-
Some(unsafe {
355-
mem::transmute::<Vec<MaybeUninit<T>>, Vec<T>>(buffer)
356-
})
357-
} else {
358-
None
359-
};
360-
cancel_on_drop.handle = None;
361-
drop(cancel_on_drop);
362-
result
363-
}) as Pin<Box<dyn Future<Output = _>>>
364-
}
365-
Handle::LocalOpen => {
366-
let (tx, rx) = oneshot::channel();
367-
entry.insert(Handle::LocalWaiting(tx));
368-
let mut cancel_on_drop = CancelReadOnDrop::<T> {
369-
handle: Some(me.handle),
370-
vtable: me.vtable,
371-
};
372-
Box::pin(async move {
373-
let result = rx.map(|v| v.ok().map(|v| *v.downcast().unwrap())).await;
374-
cancel_on_drop.handle = None;
375-
drop(cancel_on_drop);
376-
result
377-
})
378-
}
379-
Handle::LocalClosed => Box::pin(future::ready(None)),
380-
Handle::LocalReady(..) => {
381-
let Handle::LocalReady(v, waker) = entry.insert(Handle::LocalOpen) else {
382-
unreachable!()
383-
};
384-
waker.wake();
385-
Box::pin(future::ready(Some(*v.downcast().unwrap())))
386-
}
330+
me.future = Some(super::with_entry(
331+
me.handle.load(Relaxed),
332+
|entry| match entry {
333+
Entry::Vacant(_) => unreachable!(),
334+
Entry::Occupied(mut entry) => match entry.get() {
335+
Handle::Write | Handle::LocalWaiting(_) => unreachable!(),
336+
Handle::Read => {
337+
let handle = me.handle.load(Relaxed);
338+
let vtable = me.vtable;
339+
let mut cancel_on_drop = CancelReadOnDrop::<T> {
340+
handle: Some(handle),
341+
vtable,
342+
};
343+
Box::pin(async move {
344+
let mut buffer = iter::repeat_with(MaybeUninit::uninit)
345+
.take(ceiling(64 * 1024, mem::size_of::<T>()))
346+
.collect::<Vec<_>>();
347+
348+
let result =
349+
if let Some(count) = (vtable.read)(handle, &mut buffer).await {
350+
buffer.truncate(count);
351+
Some(unsafe {
352+
mem::transmute::<Vec<MaybeUninit<T>>, Vec<T>>(buffer)
353+
})
354+
} else {
355+
None
356+
};
357+
cancel_on_drop.handle = None;
358+
drop(cancel_on_drop);
359+
result
360+
}) as Pin<Box<dyn Future<Output = _>>>
361+
}
362+
Handle::LocalOpen => {
363+
let (tx, rx) = oneshot::channel();
364+
entry.insert(Handle::LocalWaiting(tx));
365+
let mut cancel_on_drop = CancelReadOnDrop::<T> {
366+
handle: Some(me.handle.load(Relaxed)),
367+
vtable: me.vtable,
368+
};
369+
Box::pin(async move {
370+
let result =
371+
rx.map(|v| v.ok().map(|v| *v.downcast().unwrap())).await;
372+
cancel_on_drop.handle = None;
373+
drop(cancel_on_drop);
374+
result
375+
})
376+
}
377+
Handle::LocalClosed => Box::pin(future::ready(None)),
378+
Handle::LocalReady(..) => {
379+
let Handle::LocalReady(v, waker) = entry.insert(Handle::LocalOpen)
380+
else {
381+
unreachable!()
382+
};
383+
waker.wake();
384+
Box::pin(future::ready(Some(*v.downcast().unwrap())))
385+
}
386+
},
387387
},
388-
}));
388+
));
389389
}
390390

391391
match me.future.as_mut().unwrap().as_mut().poll(cx) {
@@ -402,24 +402,30 @@ impl<T> Drop for StreamReader<T> {
402402
fn drop(&mut self) {
403403
self.future = None;
404404

405-
super::with_entry(self.handle, |entry| match entry {
406-
Entry::Vacant(_) => unreachable!(),
407-
Entry::Occupied(mut entry) => match entry.get_mut() {
408-
Handle::LocalReady(..) => {
409-
let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed) else {
410-
unreachable!()
411-
};
412-
waker.wake();
413-
}
414-
Handle::LocalOpen | Handle::LocalWaiting(_) => {
415-
entry.insert(Handle::LocalClosed);
416-
}
417-
Handle::Read | Handle::LocalClosed => {
418-
entry.remove();
419-
(self.vtable.close_readable)(self.handle);
420-
}
421-
Handle::Write => unreachable!(),
422-
},
423-
});
405+
match self.handle.load(Relaxed) {
406+
u32::MAX => {}
407+
handle => {
408+
super::with_entry(handle, |entry| match entry {
409+
Entry::Vacant(_) => unreachable!(),
410+
Entry::Occupied(mut entry) => match entry.get_mut() {
411+
Handle::LocalReady(..) => {
412+
let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed)
413+
else {
414+
unreachable!()
415+
};
416+
waker.wake();
417+
}
418+
Handle::LocalOpen | Handle::LocalWaiting(_) => {
419+
entry.insert(Handle::LocalClosed);
420+
}
421+
Handle::Read | Handle::LocalClosed => {
422+
entry.remove();
423+
(self.vtable.close_readable)(handle);
424+
}
425+
Handle::Write => unreachable!(),
426+
},
427+
});
428+
}
429+
}
424430
}
425431
}

0 commit comments

Comments
 (0)