99//! In the future, this crate will also provide a WIT-level API and
1010//! world in which to run debugger components.
1111
12- use std:: { any:: Any , future:: Future , pin:: Pin , sync:: Arc } ;
12+ use std:: {
13+ any:: Any ,
14+ future:: Future ,
15+ pin:: Pin ,
16+ sync:: {
17+ Arc ,
18+ atomic:: { AtomicBool , Ordering } ,
19+ } ,
20+ } ;
1321use tokio:: {
1422 sync:: { Mutex , mpsc} ,
1523 task:: JoinHandle ,
@@ -44,6 +52,13 @@ pub struct Debuggee<T: Send + 'static> {
4452 in_tx : mpsc:: Sender < Command < T > > ,
4553 out_rx : mpsc:: Receiver < Response < T > > ,
4654 handle : Option < JoinHandle < Result < ( ) > > > ,
55+ /// Flag shared with the inner handler: set to `true` by
56+ /// `interrupt()` so the next epoch yield is surfaced as an
57+ /// `Interrupted` event rather than eaten by the handler. Epoch
58+ /// yields serve two purposes, namely ensuring regular yields to
59+ /// the event loop and enacting an explicit interrupt, and this
60+ /// flag distinguishes those cases.
61+ interrupt_pending : Arc < AtomicBool > ,
4762}
4863
4964/// State machine from the perspective of the outer logic.
@@ -145,6 +160,7 @@ enum Response<T: 'static> {
145160struct HandlerInner < T : Send + ' static > {
146161 in_rx : Mutex < mpsc:: Receiver < Command < T > > > ,
147162 out_tx : mpsc:: Sender < Response < T > > ,
163+ interrupt_pending : Arc < AtomicBool > ,
148164}
149165
150166struct Handler < T : Send + ' static > ( Arc < HandlerInner < T > > ) ;
@@ -168,7 +184,17 @@ impl<T: Send + 'static> DebugHandler for Handler<T> {
168184 }
169185 DebugEvent :: Trap ( trap) => DebugRunResult :: Trap ( trap) ,
170186 DebugEvent :: Breakpoint => DebugRunResult :: Breakpoint ,
171- DebugEvent :: EpochYield => DebugRunResult :: EpochYield ,
187+ DebugEvent :: EpochYield => {
188+ // Only pause on epoch yields that were requested via
189+ // interrupt(). Other epoch ticks simply yield to the
190+ // event loop (funcionality already implemented in
191+ // core Wasmtime; no need to do that yield here in the
192+ // debug handler).
193+ if !self . 0 . interrupt_pending . swap ( false , Ordering :: SeqCst ) {
194+ return ;
195+ }
196+ DebugRunResult :: EpochYield
197+ }
172198 } ;
173199 if self . 0 . out_tx . send ( Response :: Paused ( result) ) . await . is_err ( ) {
174200 // Outer Debuggee has been dropped: just continue
@@ -226,30 +252,35 @@ impl<T: Send + 'static> Debuggee<T> {
226252 let engine = store. engine ( ) . clone ( ) ;
227253 let ( in_tx, in_rx) = mpsc:: channel ( 1 ) ;
228254 let ( out_tx, out_rx) = mpsc:: channel ( 1 ) ;
229-
230- let handle = tokio:: spawn ( async move {
231- // Create the handler that's invoked from within the async
232- // debug-event callback.
233- let out_tx_clone = out_tx. clone ( ) ;
234- let handler = Handler ( Arc :: new ( HandlerInner {
235- in_rx : Mutex :: new ( in_rx) ,
236- out_tx,
237- } ) ) ;
238-
239- // Emulate a breakpoint at startup.
240- log:: trace!( "inner debuggee task: first breakpoint" ) ;
241- handler
242- . handle ( store. as_context_mut ( ) , DebugEvent :: Breakpoint )
243- . await ;
244- log:: trace!( "inner debuggee task: first breakpoint resumed" ) ;
245-
246- // Now invoke the actual inner body.
247- store. set_debug_handler ( handler) ;
248- log:: trace!( "inner debuggee task: running `inner`" ) ;
249- let result = inner ( & mut store) . await ;
250- log:: trace!( "inner debuggee task: done with `inner`" ) ;
251- let _ = out_tx_clone. send ( Response :: Finished ( store) ) . await ;
252- result
255+ let interrupt_pending = Arc :: new ( AtomicBool :: new ( false ) ) ;
256+
257+ let handle = tokio:: spawn ( {
258+ let interrupt_pending = interrupt_pending. clone ( ) ;
259+ async move {
260+ // Create the handler that's invoked from within the async
261+ // debug-event callback.
262+ let out_tx_clone = out_tx. clone ( ) ;
263+ let handler = Handler ( Arc :: new ( HandlerInner {
264+ in_rx : Mutex :: new ( in_rx) ,
265+ out_tx,
266+ interrupt_pending,
267+ } ) ) ;
268+
269+ // Emulate a breakpoint at startup.
270+ log:: trace!( "inner debuggee task: first breakpoint" ) ;
271+ handler
272+ . handle ( store. as_context_mut ( ) , DebugEvent :: Breakpoint )
273+ . await ;
274+ log:: trace!( "inner debuggee task: first breakpoint resumed" ) ;
275+
276+ // Now invoke the actual inner body.
277+ store. set_debug_handler ( handler) ;
278+ log:: trace!( "inner debuggee task: running `inner`" ) ;
279+ let result = inner ( & mut store) . await ;
280+ log:: trace!( "inner debuggee task: done with `inner`" ) ;
281+ let _ = out_tx_clone. send ( Response :: Finished ( store) ) . await ;
282+ result
283+ }
253284 } ) ;
254285
255286 Debuggee {
@@ -258,6 +289,7 @@ impl<T: Send + 'static> Debuggee<T> {
258289 store : None ,
259290 in_tx,
260291 out_rx,
292+ interrupt_pending,
261293 handle : Some ( handle) ,
262294 }
263295 }
@@ -275,6 +307,12 @@ impl<T: Send + 'static> Debuggee<T> {
275307 & self . engine
276308 }
277309
310+ /// Get the interrupt-pending flag. Setting this to `true` causes
311+ /// the next epoch yield to surface as an `Interrupted` event.
312+ pub fn interrupt_pending ( & self ) -> & Arc < AtomicBool > {
313+ & self . interrupt_pending
314+ }
315+
278316 async fn wait_for_initial ( & mut self ) -> Result < ( ) > {
279317 if let DebuggeeState :: Initial = & self . state {
280318 // Need to receive and discard first `Paused`.
0 commit comments