2020#include "php.h"
2121
2222#include "exceptions_arginfo.h"
23+ #include "zend_common.h"
2324
2425zend_class_entry * async_ce_async_exception = NULL ;
2526zend_class_entry * async_ce_cancellation_exception = NULL ;
@@ -197,4 +198,80 @@ ZEND_API ZEND_COLD void async_composite_exception_add_exception(zend_object *com
197198 } else if (transfer ) {
198199 OBJ_RELEASE (exception );
199200 }
201+ }
202+
203+ static void exception_coroutine_finally_callback (
204+ zend_async_event_t * event ,
205+ zend_async_event_callback_t * callback ,
206+ void * result ,
207+ zend_object * exception
208+ )
209+ {
210+ zend_coroutine_t * coroutine = (zend_coroutine_t * ) event ;
211+
212+ if (coroutine -> extended_data != NULL ) {
213+ zend_object * exception_obj = coroutine -> extended_data ;
214+ coroutine -> extended_data = NULL ;
215+ zend_throw_exception_internal (exception_obj );
216+ }
217+ }
218+
219+ static void exception_coroutine_entry (void )
220+ {
221+ zend_coroutine_t * coroutine = ZEND_ASYNC_CURRENT_COROUTINE ;
222+
223+ if (UNEXPECTED (coroutine == NULL || coroutine -> extended_data == NULL )) {
224+ return ;
225+ }
226+
227+ zend_object * exception = coroutine -> extended_data ;
228+ coroutine -> extended_data = NULL ;
229+
230+ zend_throw_exception_internal (exception );
231+ }
232+
233+ bool async_spawn_and_throw (zend_object * exception , zend_async_scope_t * scope , int32_t priority )
234+ {
235+ bool need_new_scope = false;
236+
237+ if (scope == NULL ) {
238+ scope = ZEND_ASYNC_CURRENT_SCOPE ;
239+ }
240+
241+ if (ZEND_ASYNC_SCOPE_IS_CANCELLED (scope ) || ZEND_ASYNC_SCOPE_IS_CLOSED (scope )) {
242+ scope = scope -> parent_scope ;
243+ need_new_scope = true;
244+ }
245+
246+ if (scope == NULL || ZEND_ASYNC_SCOPE_IS_CLOSED (scope )) {
247+ async_warning ("To throw an exception in a coroutine, "
248+ "the Scope must be active or have a parent that is not closed." );
249+ return false;
250+ }
251+
252+ if (need_new_scope ) {
253+ scope = ZEND_ASYNC_NEW_SCOPE (scope );
254+ if (UNEXPECTED (scope == NULL )) {
255+ async_warning ("Failed to create a new Scope for throwing an exception in a coroutine." );
256+ return false;
257+ }
258+ }
259+
260+ zend_coroutine_t * coroutine = ZEND_ASYNC_SPAWN_WITH_SCOPE_EX (scope , priority );
261+ if (UNEXPECTED (coroutine == NULL )) {
262+ async_warning ("Failed to spawn a coroutine for throwing an exception." );
263+ return false;
264+ }
265+
266+ coroutine -> event .add_callback (& coroutine -> event , ZEND_ASYNC_EVENT_CALLBACK (exception_coroutine_finally_callback ));
267+ if (UNEXPECTED (EG (exception ))) {
268+ coroutine -> event .dispose (& coroutine -> event );
269+ return false;
270+ }
271+
272+ coroutine -> internal_entry = exception_coroutine_entry ;
273+ coroutine -> extended_data = exception ;
274+ GC_ADDREF (exception );
275+
276+ return true;
200277}
0 commit comments