Skip to content

Commit 3411b64

Browse files
committed
Implement async task cancellation
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 3411b64

13 files changed

Lines changed: 587 additions & 156 deletions

File tree

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)