@@ -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+ // whether hooks have been initialized
22+ ag_hooks_inited : AtomicCell < bool > ,
23+ // ag_origin_or_finalizer - stores the finalizer callback
24+ ag_finalizer : PyMutex < Option < PyObjectRef > > ,
2025}
2126type PyAsyncGenRef = PyRef < PyAsyncGen > ;
2227
@@ -37,6 +42,48 @@ 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.
51+ /// Returns Ok(()) if successful, Err if firstiter hook raised an exception.
52+ fn init_hooks ( zelf : & Py < Self > , vm : & VirtualMachine ) -> PyResult < ( ) > {
53+ // = async_gen_init_hooks
54+ if zelf. ag_hooks_inited . load ( ) {
55+ return Ok ( ( ) ) ;
56+ }
57+
58+ zelf. ag_hooks_inited . store ( true ) ;
59+
60+ // Get and store finalizer from thread-local storage
61+ let finalizer = crate :: vm:: thread:: ASYNC_GEN_FINALIZER . with_borrow ( |f| f. as_ref ( ) . cloned ( ) ) ;
62+ if let Some ( finalizer) = finalizer {
63+ * zelf. ag_finalizer . lock ( ) = Some ( finalizer) ;
64+ }
65+
66+ // Call firstiter hook
67+ let firstiter = crate :: vm:: thread:: ASYNC_GEN_FIRSTITER . with_borrow ( |f| f. as_ref ( ) . cloned ( ) ) ;
68+ if let Some ( firstiter) = firstiter {
69+ let obj: PyObjectRef = zelf. to_owned ( ) . into ( ) ;
70+ firstiter. call ( ( obj, ) , vm) ?;
71+ }
72+
73+ Ok ( ( ) )
74+ }
75+
76+ /// Call finalizer hook if set
77+ #[ allow( dead_code) ]
78+ fn call_finalizer ( zelf : & Py < Self > , vm : & VirtualMachine ) {
79+ // = gen_dealloc
80+ let finalizer = zelf. ag_finalizer . lock ( ) . clone ( ) ;
81+ if let Some ( finalizer) = finalizer
82+ && !zelf. inner . closed . load ( )
83+ {
84+ // Call finalizer, ignore any errors (PyErr_WriteUnraisable)
85+ let obj: PyObjectRef = zelf. to_owned ( ) . into ( ) ;
86+ let _ = finalizer. call ( ( obj, ) , vm) ;
4087 }
4188 }
4289
@@ -91,17 +138,23 @@ impl PyRef<PyAsyncGen> {
91138 }
92139
93140 #[ pymethod]
94- fn __anext__ ( self , vm : & VirtualMachine ) -> PyAsyncGenASend {
95- Self :: asend ( self , vm. ctx . none ( ) , vm)
141+ fn __anext__ ( self , vm : & VirtualMachine ) -> PyResult < PyAsyncGenASend > {
142+ PyAsyncGen :: init_hooks ( & self , vm) ?;
143+ Ok ( PyAsyncGenASend {
144+ ag : self ,
145+ state : AtomicCell :: new ( AwaitableState :: Init ) ,
146+ value : vm. ctx . none ( ) ,
147+ } )
96148 }
97149
98150 #[ pymethod]
99- const fn asend ( self , value : PyObjectRef , _vm : & VirtualMachine ) -> PyAsyncGenASend {
100- PyAsyncGenASend {
151+ fn asend ( self , value : PyObjectRef , vm : & VirtualMachine ) -> PyResult < PyAsyncGenASend > {
152+ PyAsyncGen :: init_hooks ( & self , vm) ?;
153+ Ok ( PyAsyncGenASend {
101154 ag : self ,
102155 state : AtomicCell :: new ( AwaitableState :: Init ) ,
103156 value,
104- }
157+ } )
105158 }
106159
107160 #[ pymethod]
@@ -111,8 +164,9 @@ impl PyRef<PyAsyncGen> {
111164 exc_val : OptionalArg ,
112165 exc_tb : OptionalArg ,
113166 vm : & VirtualMachine ,
114- ) -> PyAsyncGenAThrow {
115- PyAsyncGenAThrow {
167+ ) -> PyResult < PyAsyncGenAThrow > {
168+ PyAsyncGen :: init_hooks ( & self , vm) ?;
169+ Ok ( PyAsyncGenAThrow {
116170 ag : self ,
117171 aclose : false ,
118172 state : AtomicCell :: new ( AwaitableState :: Init ) ,
@@ -121,12 +175,13 @@ impl PyRef<PyAsyncGen> {
121175 exc_val. unwrap_or_none ( vm) ,
122176 exc_tb. unwrap_or_none ( vm) ,
123177 ) ,
124- }
178+ } )
125179 }
126180
127181 #[ pymethod]
128- fn aclose ( self , vm : & VirtualMachine ) -> PyAsyncGenAThrow {
129- PyAsyncGenAThrow {
182+ fn aclose ( self , vm : & VirtualMachine ) -> PyResult < PyAsyncGenAThrow > {
183+ PyAsyncGen :: init_hooks ( & self , vm) ?;
184+ Ok ( PyAsyncGenAThrow {
130185 ag : self ,
131186 aclose : true ,
132187 state : AtomicCell :: new ( AwaitableState :: Init ) ,
@@ -135,7 +190,7 @@ impl PyRef<PyAsyncGen> {
135190 vm. ctx . none ( ) ,
136191 vm. ctx . none ( ) ,
137192 ) ,
138- }
193+ } )
139194 }
140195}
141196
0 commit comments