11use crate :: {
22 AsObject , Py , PyObject , PyObjectRef , PyResult , TryFromObject , VirtualMachine ,
3- builtins:: { PyBaseExceptionRef , PyStrRef } ,
3+ builtins:: PyStrRef ,
44 common:: lock:: PyMutex ,
55 exceptions:: types:: PyBaseException ,
66 frame:: { ExecutionResult , FrameOwner , FrameRef } ,
77 function:: OptionalArg ,
8- object:: { Traverse , TraverseFn } ,
8+ object:: { PyAtomicRef , Traverse , TraverseFn } ,
99 protocol:: PyIterReturn ,
1010} ;
1111use crossbeam_utils:: atomic:: AtomicCell ;
@@ -36,15 +36,17 @@ pub struct Coro {
3636 // _weakreflist
3737 name : PyMutex < PyStrRef > ,
3838 qualname : PyMutex < PyStrRef > ,
39- exception : PyMutex < Option < PyBaseExceptionRef > > , // exc_state
39+ exception : PyAtomicRef < Option < PyBaseException > > , // exc_state
4040}
4141
4242unsafe impl Traverse for Coro {
4343 fn traverse ( & self , tracer_fn : & mut TraverseFn < ' _ > ) {
4444 self . frame . traverse ( tracer_fn) ;
4545 self . name . traverse ( tracer_fn) ;
4646 self . qualname . traverse ( tracer_fn) ;
47- self . exception . traverse ( tracer_fn) ;
47+ if let Some ( exc) = self . exception . deref ( ) {
48+ exc. traverse ( tracer_fn) ;
49+ }
4850 }
4951}
5052
@@ -65,7 +67,7 @@ impl Coro {
6567 frame,
6668 closed : AtomicCell :: new ( false ) ,
6769 running : AtomicCell :: new ( false ) ,
68- exception : PyMutex :: default ( ) ,
70+ exception : PyAtomicRef :: from ( None ) ,
6971 name : PyMutex :: new ( name) ,
7072 qualname : PyMutex :: new ( qualname) ,
7173 }
@@ -92,33 +94,20 @@ impl Coro {
9294 func : F ,
9395 ) -> PyResult < ExecutionResult >
9496 where
95- F : FnOnce ( FrameRef ) -> PyResult < ExecutionResult > ,
97+ F : FnOnce ( & FrameRef ) -> PyResult < ExecutionResult > ,
9698 {
9799 if self . running . compare_exchange ( false , true ) . is_err ( ) {
98100 return Err ( vm. new_value_error ( format ! ( "{} already executing" , gen_name( jen, vm) ) ) ) ;
99101 }
100102
101- // swap exception state
102- // Get generator's saved exception state from last yield
103- let gen_exc = self . exception . lock ( ) . take ( ) ;
104-
105- // Use a slot to capture generator's exception state before with_frame pops
106- let exception_slot = & self . exception ;
103+ // SAFETY: running.compare_exchange guarantees exclusive access
104+ let gen_exc = unsafe { self . exception . swap ( None ) } ;
105+ let exception_ptr = & self . exception as * const PyAtomicRef < Option < PyBaseException > > ;
107106
108- // Run the generator frame
109- // with_frame does push_exception(None) which creates a new exception context
110- // The caller's exception remains in the chain via prev, so topmost_exception()
111- // will find it if generator's exception is None
112- let result = vm. with_frame ( self . frame . clone ( ) , |f| {
113- // with_frame pushed None, creating: { exc: None, prev: caller's exc_info }
114- // Pop None and push generator's exception instead
115- // This maintains the chain: { exc: gen_exc, prev: caller's exc_info }
116- vm. pop_exception ( ) ;
117- vm. push_exception ( gen_exc) ;
107+ let result = vm. resume_gen_frame ( & self . frame , gen_exc, |f| {
118108 let result = func ( f) ;
119- // Save generator's exception state BEFORE with_frame pops
120- // This is the generator's current exception context
121- * exception_slot. lock ( ) = vm. current_exception ( ) ;
109+ // SAFETY: exclusive access guaranteed by running flag
110+ let _old = unsafe { ( * exception_ptr) . swap ( vm. current_exception ( ) ) } ;
122111 result
123112 } ) ;
124113
0 commit comments