Skip to content

Commit 16ad298

Browse files
authored
Implement async task cancellation (#1264)
This commit implements async task cancellation in Rust for subtasks created through imported calls. This involved some extra bindings-related code to drop both lists and owned handles (effectively a full destructor) along with some refactorings of how waitables are handled due to new states that can pop up.
1 parent 4fd8f2c commit 16ad298

File tree

13 files changed

+587
-156
lines changed

13 files changed

+587
-156
lines changed

crates/core/src/abi.rs

Lines changed: 95 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,10 @@ def_instruction! {
554554
blocks: usize,
555555
} : [1] => [0],
556556

557+
/// Deallocates the language-specific handle representation on the top
558+
/// of the stack. Used for async imports.
559+
DropHandle { ty: &'a Type } : [1] => [0],
560+
557561
/// Call `task.return` for an async-lifted export.
558562
///
559563
/// This will call core wasm import `name` which will be mapped to
@@ -783,33 +787,37 @@ pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindge
783787
/// a list or a string primarily.
784788
pub fn guest_export_needs_post_return(resolve: &Resolve, func: &Function) -> bool {
785789
func.result
786-
.map(|t| needs_post_return(resolve, &t))
790+
.map(|t| needs_deallocate(resolve, &t, Deallocate::Lists))
787791
.unwrap_or(false)
788792
}
789793

790-
fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool {
794+
fn needs_deallocate(resolve: &Resolve, ty: &Type, what: Deallocate) -> bool {
791795
match ty {
792796
Type::String => true,
793797
Type::ErrorContext => true,
794798
Type::Id(id) => match &resolve.types[*id].kind {
795799
TypeDefKind::List(_) => true,
796-
TypeDefKind::Type(t) => needs_post_return(resolve, t),
797-
TypeDefKind::Handle(_) => false,
800+
TypeDefKind::Type(t) => needs_deallocate(resolve, t, what),
801+
TypeDefKind::Handle(Handle::Own(_)) => what.handles(),
802+
TypeDefKind::Handle(Handle::Borrow(_)) => false,
798803
TypeDefKind::Resource => false,
799-
TypeDefKind::Record(r) => r.fields.iter().any(|f| needs_post_return(resolve, &f.ty)),
800-
TypeDefKind::Tuple(t) => t.types.iter().any(|t| needs_post_return(resolve, t)),
804+
TypeDefKind::Record(r) => r
805+
.fields
806+
.iter()
807+
.any(|f| needs_deallocate(resolve, &f.ty, what)),
808+
TypeDefKind::Tuple(t) => t.types.iter().any(|t| needs_deallocate(resolve, t, what)),
801809
TypeDefKind::Variant(t) => t
802810
.cases
803811
.iter()
804812
.filter_map(|t| t.ty.as_ref())
805-
.any(|t| needs_post_return(resolve, t)),
806-
TypeDefKind::Option(t) => needs_post_return(resolve, t),
813+
.any(|t| needs_deallocate(resolve, t, what)),
814+
TypeDefKind::Option(t) => needs_deallocate(resolve, t, what),
807815
TypeDefKind::Result(t) => [&t.ok, &t.err]
808816
.iter()
809817
.filter_map(|t| t.as_ref())
810-
.any(|t| needs_post_return(resolve, t)),
818+
.any(|t| needs_deallocate(resolve, t, what)),
811819
TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false,
812-
TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false,
820+
TypeDefKind::Future(_) | TypeDefKind::Stream(_) => what.handles(),
813821
TypeDefKind::Unknown => unreachable!(),
814822
},
815823

@@ -836,7 +844,18 @@ pub fn deallocate_lists_in_types<B: Bindgen>(
836844
ptr: B::Operand,
837845
bindgen: &mut B,
838846
) {
839-
Generator::new(resolve, bindgen).deallocate_lists_in_types(types, ptr);
847+
Generator::new(resolve, bindgen).deallocate_in_types(types, ptr, Deallocate::Lists);
848+
}
849+
850+
/// Generate instructions in `bindgen` to deallocate all lists in `ptr` where
851+
/// that's a pointer to a sequence of `types` stored in linear memory.
852+
pub fn deallocate_lists_and_own_in_types<B: Bindgen>(
853+
resolve: &Resolve,
854+
types: &[Type],
855+
ptr: B::Operand,
856+
bindgen: &mut B,
857+
) {
858+
Generator::new(resolve, bindgen).deallocate_in_types(types, ptr, Deallocate::ListsAndOwn);
840859
}
841860

842861
#[derive(Copy, Clone)]
@@ -845,6 +864,25 @@ pub enum Realloc {
845864
Export(&'static str),
846865
}
847866

867+
/// What to deallocate in various `deallocate_*` methods.
868+
#[derive(Copy, Clone)]
869+
enum Deallocate {
870+
/// Only deallocate lists.
871+
Lists,
872+
/// Deallocate lists and owned resources such as `own<T>` and
873+
/// futures/streams.
874+
ListsAndOwn,
875+
}
876+
877+
impl Deallocate {
878+
fn handles(&self) -> bool {
879+
match self {
880+
Deallocate::Lists => false,
881+
Deallocate::ListsAndOwn => true,
882+
}
883+
}
884+
}
885+
848886
struct Generator<'a, B: Bindgen> {
849887
bindgen: &'a mut B,
850888
resolve: &'a Resolve,
@@ -1166,14 +1204,14 @@ impl<'a, B: Bindgen> Generator<'a, B> {
11661204

11671205
let mut types = Vec::new();
11681206
types.extend(func.result);
1169-
self.deallocate_lists_in_types(&types, addr);
1207+
self.deallocate_in_types(&types, addr, Deallocate::Lists);
11701208

11711209
self.emit(&Instruction::Return { func, amt: 0 });
11721210
}
11731211

1174-
fn deallocate_lists_in_types(&mut self, types: &[Type], addr: B::Operand) {
1212+
fn deallocate_in_types(&mut self, types: &[Type], addr: B::Operand, what: Deallocate) {
11751213
for (offset, ty) in self.bindgen.sizes().field_offsets(types) {
1176-
self.deallocate(ty, addr.clone(), offset);
1214+
self.deallocate(ty, addr.clone(), offset, what);
11771215
}
11781216

11791217
assert!(
@@ -1973,12 +2011,18 @@ impl<'a, B: Bindgen> Generator<'a, B> {
19732011
});
19742012
}
19752013

1976-
fn deallocate(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) {
2014+
fn deallocate(
2015+
&mut self,
2016+
ty: &Type,
2017+
addr: B::Operand,
2018+
offset: ArchitectureSize,
2019+
what: Deallocate,
2020+
) {
19772021
use Instruction::*;
19782022

19792023
// No need to execute any instructions if this type itself doesn't
19802024
// require any form of post-return.
1981-
if !needs_post_return(self.resolve, ty) {
2025+
if !needs_deallocate(self.resolve, ty, what) {
19822026
return;
19832027
}
19842028

@@ -2008,7 +2052,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
20082052
| Type::ErrorContext => {}
20092053

20102054
Type::Id(id) => match &self.resolve.types[id].kind {
2011-
TypeDefKind::Type(t) => self.deallocate(t, addr, offset),
2055+
TypeDefKind::Type(t) => self.deallocate(t, addr, offset, what),
20122056

20132057
TypeDefKind::List(element) => {
20142058
self.stack.push(addr.clone());
@@ -2021,30 +2065,36 @@ impl<'a, B: Bindgen> Generator<'a, B> {
20212065
self.push_block();
20222066
self.emit(&IterBasePointer);
20232067
let elemaddr = self.stack.pop().unwrap();
2024-
self.deallocate(element, elemaddr, Default::default());
2068+
self.deallocate(element, elemaddr, Default::default(), what);
20252069
self.finish_block(0);
20262070

20272071
self.emit(&Instruction::GuestDeallocateList { element });
20282072
}
20292073

2030-
TypeDefKind::Handle(_) => {
2031-
todo!()
2074+
TypeDefKind::Handle(Handle::Own(_))
2075+
| TypeDefKind::Future(_)
2076+
| TypeDefKind::Stream(_)
2077+
if what.handles() =>
2078+
{
2079+
self.read_from_memory(ty, addr, offset);
2080+
self.emit(&DropHandle { ty });
20322081
}
20332082

2034-
TypeDefKind::Resource => {
2035-
todo!()
2036-
}
2083+
TypeDefKind::Handle(Handle::Own(_)) => unreachable!(),
2084+
TypeDefKind::Handle(Handle::Borrow(_)) => unreachable!(),
2085+
TypeDefKind::Resource => unreachable!(),
20372086

20382087
TypeDefKind::Record(record) => {
20392088
self.deallocate_fields(
20402089
&record.fields.iter().map(|f| f.ty).collect::<Vec<_>>(),
20412090
addr,
20422091
offset,
2092+
what,
20432093
);
20442094
}
20452095

20462096
TypeDefKind::Tuple(tuple) => {
2047-
self.deallocate_fields(&tuple.types, addr, offset);
2097+
self.deallocate_fields(&tuple.types, addr, offset, what);
20482098
}
20492099

20502100
TypeDefKind::Flags(_) => {}
@@ -2055,26 +2105,33 @@ impl<'a, B: Bindgen> Generator<'a, B> {
20552105
addr,
20562106
variant.tag(),
20572107
variant.cases.iter().map(|c| c.ty.as_ref()),
2108+
what,
20582109
);
20592110
self.emit(&GuestDeallocateVariant {
20602111
blocks: variant.cases.len(),
20612112
});
20622113
}
20632114

20642115
TypeDefKind::Option(t) => {
2065-
self.deallocate_variant(offset, addr, Int::U8, [None, Some(t)]);
2116+
self.deallocate_variant(offset, addr, Int::U8, [None, Some(t)], what);
20662117
self.emit(&GuestDeallocateVariant { blocks: 2 });
20672118
}
20682119

20692120
TypeDefKind::Result(e) => {
2070-
self.deallocate_variant(offset, addr, Int::U8, [e.ok.as_ref(), e.err.as_ref()]);
2121+
self.deallocate_variant(
2122+
offset,
2123+
addr,
2124+
Int::U8,
2125+
[e.ok.as_ref(), e.err.as_ref()],
2126+
what,
2127+
);
20712128
self.emit(&GuestDeallocateVariant { blocks: 2 });
20722129
}
20732130

20742131
TypeDefKind::Enum(_) => {}
20752132

2076-
TypeDefKind::Future(_) => todo!("read future from memory"),
2077-
TypeDefKind::Stream(_) => todo!("read stream from memory"),
2133+
TypeDefKind::Future(_) => unreachable!(),
2134+
TypeDefKind::Stream(_) => unreachable!(),
20782135
TypeDefKind::Unknown => unreachable!(),
20792136
},
20802137
}
@@ -2086,22 +2143,29 @@ impl<'a, B: Bindgen> Generator<'a, B> {
20862143
addr: B::Operand,
20872144
tag: Int,
20882145
cases: impl IntoIterator<Item = Option<&'b Type>> + Clone,
2146+
what: Deallocate,
20892147
) {
20902148
self.stack.push(addr.clone());
20912149
self.load_intrepr(offset, tag);
20922150
let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()));
20932151
for ty in cases {
20942152
self.push_block();
20952153
if let Some(ty) = ty {
2096-
self.deallocate(ty, addr.clone(), payload_offset);
2154+
self.deallocate(ty, addr.clone(), payload_offset, what);
20972155
}
20982156
self.finish_block(0);
20992157
}
21002158
}
21012159

2102-
fn deallocate_fields(&mut self, tys: &[Type], addr: B::Operand, offset: ArchitectureSize) {
2160+
fn deallocate_fields(
2161+
&mut self,
2162+
tys: &[Type],
2163+
addr: B::Operand,
2164+
offset: ArchitectureSize,
2165+
what: Deallocate,
2166+
) {
21032167
for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys) {
2104-
self.deallocate(ty, addr.clone(), offset + (field_offset));
2168+
self.deallocate(ty, addr.clone(), offset + (field_offset), what);
21052169
}
21062170
}
21072171
}

crates/csharp/src/function.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,9 @@ impl Bindgen for FunctionBindgen<'_, '_> {
12621262
| Instruction::StreamLower { .. }
12631263
| Instruction::StreamLift { .. }
12641264
| Instruction::ErrorContextLower { .. }
1265-
| Instruction::ErrorContextLift { .. } => todo!(),
1265+
| Instruction::ErrorContextLift { .. }
1266+
| Instruction::DropHandle { .. }
1267+
=> todo!(),
12661268
}
12671269
}
12681270

0 commit comments

Comments
 (0)