Skip to content

Commit c39e049

Browse files
committed
Fix cancel-safety of EventFuture.
1 parent b3fc04d commit c39e049

1 file changed

Lines changed: 75 additions & 48 deletions

File tree

  • crates/debugger/src/host

crates/debugger/src/host/api.rs

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::host::opaque::OpaqueDebugger;
44
use crate::host::wit;
55
use crate::{DebugRunResult, host::bindings::wasm_type_to_val_type};
6+
use std::pin::Pin;
67
use wasmtime::{
78
Engine, ExnRef, FrameHandle, Func, Global, Instance, Memory, Module, OwnedRooted, Result,
89
Table, Tag, Val, component::Resource, component::ResourceTable,
@@ -65,46 +66,81 @@ impl WasmValue {
6566

6667
/// Representation of an async debug event that the debugger is
6768
/// waiting on.
69+
///
70+
/// Cancel-safety: the non-cancel-safe OpaqueDebugger async methods
71+
/// are called inside an `async move` block that owns the debugger.
72+
/// `ready()` merely polls this stored future, which is always safe to
73+
/// re-poll after cancelation. The debugger is returned in the `Done`
74+
/// state and extracted by `finish()`.
6875
pub struct EventFuture {
69-
inner: Box<dyn OpaqueDebugger + Send + 'static>,
7076
state: EventFutureState,
7177
}
7278

7379
enum EventFutureState {
74-
SingleStep(Option<wit::ResumptionValue>),
75-
Continue(Option<wit::ResumptionValue>),
76-
Done(Result<DebugRunResult>),
80+
/// The future is running; owns the debugger.
81+
Running(
82+
Pin<
83+
Box<
84+
dyn Future<
85+
Output = (
86+
Box<dyn OpaqueDebugger + Send + 'static>,
87+
Result<DebugRunResult>,
88+
),
89+
> + Send,
90+
>,
91+
>,
92+
),
93+
/// The future has completed; debugger is ready to be returned.
94+
Done {
95+
inner: Box<dyn OpaqueDebugger + Send + 'static>,
96+
result: Option<Result<DebugRunResult>>,
97+
},
98+
}
99+
100+
impl EventFuture {
101+
fn new_single_step(
102+
mut inner: Box<dyn OpaqueDebugger + Send + 'static>,
103+
resumption: wit::ResumptionValue,
104+
) -> Self {
105+
EventFuture {
106+
state: EventFutureState::Running(Box::pin(async move {
107+
if let Err(e) = inner.handle_resumption(&resumption).await {
108+
return (inner, Err(e));
109+
}
110+
let result = inner.single_step().await;
111+
(inner, result)
112+
})),
113+
}
114+
}
115+
116+
fn new_continue(
117+
mut inner: Box<dyn OpaqueDebugger + Send + 'static>,
118+
resumption: wit::ResumptionValue,
119+
) -> Self {
120+
EventFuture {
121+
state: EventFutureState::Running(Box::pin(async move {
122+
if let Err(e) = inner.handle_resumption(&resumption).await {
123+
return (inner, Err(e));
124+
}
125+
let result = inner.continue_().await;
126+
(inner, result)
127+
})),
128+
}
129+
}
77130
}
78131

79132
#[async_trait::async_trait]
80133
impl wasmtime_wasi_io::poll::Pollable for EventFuture {
81134
async fn ready(&mut self) {
82135
match &mut self.state {
83-
EventFutureState::SingleStep(resumption) => {
84-
if let Some(r) = resumption.as_ref() {
85-
if let Err(e) = self.inner.handle_resumption(r).await {
86-
self.state = EventFutureState::Done(Err(e));
87-
return;
88-
}
89-
// Remove only after success, for cancel safety.
90-
resumption.take();
91-
}
92-
let result = self.inner.single_step().await;
93-
self.state = EventFutureState::Done(result);
136+
EventFutureState::Running(future) => {
137+
let (inner, result) = future.await;
138+
self.state = EventFutureState::Done {
139+
inner,
140+
result: Some(result),
141+
};
94142
}
95-
EventFutureState::Continue(resumption) => {
96-
if let Some(r) = resumption.as_ref() {
97-
if let Err(e) = self.inner.handle_resumption(r).await {
98-
self.state = EventFutureState::Done(Err(e));
99-
return;
100-
}
101-
// Remove only after success, for cancel safety.
102-
resumption.take();
103-
}
104-
let result = self.inner.continue_().await;
105-
self.state = EventFutureState::Done(result);
106-
}
107-
EventFutureState::Done(_) => {}
143+
EventFutureState::Done { .. } => {}
108144
}
109145
}
110146
}
@@ -179,13 +215,7 @@ impl wit::HostDebuggee for ResourceTable {
179215
resumption: wit::ResumptionValue,
180216
) -> Result<Resource<EventFuture>> {
181217
let d = self.get_mut(&debuggee).unwrap().inner.take().unwrap();
182-
Ok(self.push_child(
183-
EventFuture {
184-
inner: d,
185-
state: EventFutureState::SingleStep(Some(resumption)),
186-
},
187-
&debuggee,
188-
)?)
218+
Ok(self.push_child(EventFuture::new_single_step(d, resumption), &debuggee)?)
189219
}
190220

191221
async fn continue_(
@@ -194,13 +224,7 @@ impl wit::HostDebuggee for ResourceTable {
194224
resumption: wit::ResumptionValue,
195225
) -> Result<Resource<EventFuture>> {
196226
let d = self.get_mut(&debuggee).unwrap().inner.take().unwrap();
197-
Ok(self.push_child(
198-
EventFuture {
199-
inner: d,
200-
state: EventFutureState::Continue(Some(resumption)),
201-
},
202-
&debuggee,
203-
)?)
227+
Ok(self.push_child(EventFuture::new_continue(d, resumption), &debuggee)?)
204228
}
205229

206230
async fn exit_frames(&mut self, debuggee: Resource<Debuggee>) -> Result<Vec<Resource<Frame>>> {
@@ -245,14 +269,17 @@ impl wit::HostEventFuture for ResourceTable {
245269
) -> Result<wit::Event> {
246270
let mut f = self.delete(self_)?;
247271
f.ready().await;
248-
let EventFuture { inner, state } = f;
249-
self.get_mut(&debuggee)?.inner = Some(inner);
250-
match state {
251-
EventFutureState::SingleStep(..) | EventFutureState::Continue(..) => {
272+
match f.state {
273+
EventFutureState::Running(..) => {
252274
unreachable!("ready() cannot return until setting Done state")
253275
}
254-
EventFutureState::Done(Ok(result)) => Ok(result_to_event(self, result)?),
255-
EventFutureState::Done(Err(e)) => Err(e),
276+
EventFutureState::Done { inner, result } => {
277+
self.get_mut(&debuggee)?.inner = Some(inner);
278+
match result.unwrap() {
279+
Ok(result) => Ok(result_to_event(self, result)?),
280+
Err(e) => Err(e),
281+
}
282+
}
256283
}
257284
}
258285

0 commit comments

Comments
 (0)