@@ -3,6 +3,7 @@ use crate::{
33 AsObject , Context , Py , PyObjectRef , PyPayload , PyRef , PyResult , VirtualMachine ,
44 builtins:: PyBaseExceptionRef ,
55 class:: PyClassImpl ,
6+ common:: lock:: PyMutex ,
67 coroutine:: Coro ,
78 frame:: FrameRef ,
89 function:: OptionalArg ,
@@ -17,6 +18,10 @@ use crossbeam_utils::atomic::AtomicCell;
1718pub struct PyAsyncGen {
1819 inner : Coro ,
1920 running_async : AtomicCell < bool > ,
21+ // CPython: ag_hooks_inited - whether hooks have been initialized
22+ ag_hooks_inited : AtomicCell < bool > ,
23+ // CPython: ag_origin_or_finalizer - stores the finalizer callback
24+ ag_finalizer : PyMutex < Option < PyObjectRef > > ,
2025}
2126type PyAsyncGenRef = PyRef < PyAsyncGen > ;
2227
@@ -37,6 +42,46 @@ impl PyAsyncGen {
3742 Self {
3843 inner : Coro :: new ( frame, name, qualname) ,
3944 running_async : AtomicCell :: new ( false ) ,
45+ ag_hooks_inited : AtomicCell :: new ( false ) ,
46+ ag_finalizer : PyMutex :: new ( None ) ,
47+ }
48+ }
49+
50+ /// Initialize async generator hooks (CPython: async_gen_init_hooks).
51+ /// Returns Ok(()) if successful, Err if firstiter hook raised an exception.
52+ fn init_hooks ( zelf : & Py < Self > , vm : & VirtualMachine ) -> PyResult < ( ) > {
53+ if zelf. ag_hooks_inited . load ( ) {
54+ return Ok ( ( ) ) ;
55+ }
56+
57+ zelf. ag_hooks_inited . store ( true ) ;
58+
59+ // Get and store finalizer from thread-local storage
60+ let finalizer = crate :: vm:: thread:: ASYNC_GEN_FINALIZER . with_borrow ( |f| f. as_ref ( ) . cloned ( ) ) ;
61+ if let Some ( finalizer) = finalizer {
62+ * zelf. ag_finalizer . lock ( ) = Some ( finalizer) ;
63+ }
64+
65+ // Call firstiter hook
66+ let firstiter = crate :: vm:: thread:: ASYNC_GEN_FIRSTITER . with_borrow ( |f| f. as_ref ( ) . cloned ( ) ) ;
67+ if let Some ( firstiter) = firstiter {
68+ let obj: PyObjectRef = zelf. to_owned ( ) . into ( ) ;
69+ firstiter. call ( ( obj, ) , vm) ?;
70+ }
71+
72+ Ok ( ( ) )
73+ }
74+
75+ /// Call finalizer hook if set (CPython: gen_dealloc for async generators)
76+ #[ allow( dead_code) ]
77+ fn call_finalizer ( zelf : & Py < Self > , vm : & VirtualMachine ) {
78+ let finalizer = zelf. ag_finalizer . lock ( ) . clone ( ) ;
79+ if let Some ( finalizer) = finalizer {
80+ if !zelf. inner . closed . load ( ) {
81+ // Call finalizer, ignore any errors (CPython: PyErr_WriteUnraisable)
82+ let obj: PyObjectRef = zelf. to_owned ( ) . into ( ) ;
83+ let _ = finalizer. call ( ( obj, ) , vm) ;
84+ }
4085 }
4186 }
4287
@@ -91,17 +136,23 @@ impl PyRef<PyAsyncGen> {
91136 }
92137
93138 #[ pymethod]
94- fn __anext__ ( self , vm : & VirtualMachine ) -> PyAsyncGenASend {
95- Self :: asend ( self , vm. ctx . none ( ) , vm)
139+ fn __anext__ ( self , vm : & VirtualMachine ) -> PyResult < PyAsyncGenASend > {
140+ PyAsyncGen :: init_hooks ( & self , vm) ?;
141+ Ok ( PyAsyncGenASend {
142+ ag : self ,
143+ state : AtomicCell :: new ( AwaitableState :: Init ) ,
144+ value : vm. ctx . none ( ) ,
145+ } )
96146 }
97147
98148 #[ pymethod]
99- const fn asend ( self , value : PyObjectRef , _vm : & VirtualMachine ) -> PyAsyncGenASend {
100- PyAsyncGenASend {
149+ fn asend ( self , value : PyObjectRef , vm : & VirtualMachine ) -> PyResult < PyAsyncGenASend > {
150+ PyAsyncGen :: init_hooks ( & self , vm) ?;
151+ Ok ( PyAsyncGenASend {
101152 ag : self ,
102153 state : AtomicCell :: new ( AwaitableState :: Init ) ,
103154 value,
104- }
155+ } )
105156 }
106157
107158 #[ pymethod]
@@ -111,8 +162,9 @@ impl PyRef<PyAsyncGen> {
111162 exc_val : OptionalArg ,
112163 exc_tb : OptionalArg ,
113164 vm : & VirtualMachine ,
114- ) -> PyAsyncGenAThrow {
115- PyAsyncGenAThrow {
165+ ) -> PyResult < PyAsyncGenAThrow > {
166+ PyAsyncGen :: init_hooks ( & self , vm) ?;
167+ Ok ( PyAsyncGenAThrow {
116168 ag : self ,
117169 aclose : false ,
118170 state : AtomicCell :: new ( AwaitableState :: Init ) ,
@@ -121,12 +173,13 @@ impl PyRef<PyAsyncGen> {
121173 exc_val. unwrap_or_none ( vm) ,
122174 exc_tb. unwrap_or_none ( vm) ,
123175 ) ,
124- }
176+ } )
125177 }
126178
127179 #[ pymethod]
128- fn aclose ( self , vm : & VirtualMachine ) -> PyAsyncGenAThrow {
129- PyAsyncGenAThrow {
180+ fn aclose ( self , vm : & VirtualMachine ) -> PyResult < PyAsyncGenAThrow > {
181+ PyAsyncGen :: init_hooks ( & self , vm) ?;
182+ Ok ( PyAsyncGenAThrow {
130183 ag : self ,
131184 aclose : true ,
132185 state : AtomicCell :: new ( AwaitableState :: Init ) ,
@@ -135,7 +188,7 @@ impl PyRef<PyAsyncGen> {
135188 vm. ctx . none ( ) ,
136189 vm. ctx . none ( ) ,
137190 ) ,
138- }
191+ } )
139192 }
140193}
141194
0 commit comments