Allow creating Graceful/UnwindExit and use when destroying a fiber (#7174)

Direct creation of GracefulExit allows the the special exception object to be transfered and thrown into a destroyed fiber using the same path as any other exception thrown into a fiber instead of needing to check for a flag.
This commit is contained in:
Aaron Piotrowski 2021-06-28 15:23:34 -05:00 committed by GitHub
parent 773e9ba5d6
commit 66442a51d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 20 additions and 10 deletions

View File

@ -1002,10 +1002,20 @@ ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception) /* {{{ */
} }
/* }}} */ /* }}} */
ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void)
{
return zend_objects_new(&zend_ce_unwind_exit);
}
ZEND_API ZEND_COLD zend_object *zend_create_graceful_exit(void)
{
return zend_objects_new(&zend_ce_graceful_exit);
}
ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) ZEND_API ZEND_COLD void zend_throw_unwind_exit(void)
{ {
ZEND_ASSERT(!EG(exception)); ZEND_ASSERT(!EG(exception));
EG(exception) = zend_objects_new(&zend_ce_unwind_exit); EG(exception) = zend_create_unwind_exit();
EG(opline_before_exception) = EG(current_execute_data)->opline; EG(opline_before_exception) = EG(current_execute_data)->opline;
EG(current_execute_data)->opline = EG(exception_op); EG(current_execute_data)->opline = EG(exception_op);
} }
@ -1013,7 +1023,7 @@ ZEND_API ZEND_COLD void zend_throw_unwind_exit(void)
ZEND_API ZEND_COLD void zend_throw_graceful_exit(void) ZEND_API ZEND_COLD void zend_throw_graceful_exit(void)
{ {
ZEND_ASSERT(!EG(exception)); ZEND_ASSERT(!EG(exception));
EG(exception) = zend_objects_new(&zend_ce_graceful_exit); EG(exception) = zend_create_graceful_exit();
EG(opline_before_exception) = EG(current_execute_data)->opline; EG(opline_before_exception) = EG(current_execute_data)->opline;
EG(current_execute_data)->opline = EG(exception_op); EG(current_execute_data)->opline = EG(exception_op);
} }

View File

@ -71,6 +71,8 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int
ZEND_NORETURN void zend_exception_uncaught_error(const char *prefix, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2); ZEND_NORETURN void zend_exception_uncaught_error(const char *prefix, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2);
ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main); ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main);
ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void);
ZEND_API ZEND_COLD zend_object *zend_create_graceful_exit(void);
ZEND_API ZEND_COLD void zend_throw_unwind_exit(void); ZEND_API ZEND_COLD void zend_throw_unwind_exit(void);
ZEND_API ZEND_COLD void zend_throw_graceful_exit(void); ZEND_API ZEND_COLD void zend_throw_graceful_exit(void);
ZEND_API bool zend_is_unwind_exit(const zend_object *ex); ZEND_API bool zend_is_unwind_exit(const zend_object *ex);

View File

@ -539,9 +539,14 @@ static void zend_fiber_object_destroy(zend_object *object)
zend_object *exception = EG(exception); zend_object *exception = EG(exception);
EG(exception) = NULL; EG(exception) = NULL;
zval graceful_exit;
ZVAL_OBJ(&graceful_exit, zend_create_graceful_exit());
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED; fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
zend_fiber_transfer transfer = zend_fiber_resume(fiber, NULL, false); zend_fiber_transfer transfer = zend_fiber_resume(fiber, &graceful_exit, true);
zval_ptr_dtor(&graceful_exit);
if (transfer.flags & ZEND_FIBER_TRANSFER_FLAG_ERROR) { if (transfer.flags & ZEND_FIBER_TRANSFER_FLAG_ERROR) {
EG(exception) = Z_OBJ(transfer.value); EG(exception) = Z_OBJ(transfer.value);
@ -659,13 +664,6 @@ ZEND_METHOD(Fiber, suspend)
zend_fiber_transfer transfer = zend_fiber_suspend(fiber, value); zend_fiber_transfer transfer = zend_fiber_suspend(fiber, value);
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
// This occurs when the fiber is GC'ed while suspended.
zval_ptr_dtor(&transfer.value);
zend_throw_graceful_exit();
RETURN_THROWS();
}
zend_fiber_delegate_transfer_result(&transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU); zend_fiber_delegate_transfer_result(&transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU);
} }