Skip to content

Commit 3939c9b

Browse files
authored
Add internal Fiber API (php#7045)
This additional internal fiber API creates and manipulates a Fiber object, allowing any internal function to start, resume, or suspend a fiber. The existing zend_fiber_context API allows custom C-based fiber creation using the bundled switching context, but does not interact with the PHP VM. This API behaves the same as calling Fiber object methods from user code, switching EGs, and triggering the fiber switch observer. In general, the Fiber object methods call these new API methods.
1 parent 0e2ac30 commit 3939c9b

File tree

2 files changed

+70
-30
lines changed

2 files changed

+70
-30
lines changed

Zend/zend_fibers.c

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current)
266266
current->caller = transfer.context;
267267
}
268268

269-
static void zend_fiber_suspend(zend_fiber *fiber)
269+
static void zend_fiber_suspend_from(zend_fiber *fiber)
270270
{
271271
zend_vm_stack stack;
272272
size_t stack_page_size;
@@ -445,6 +445,18 @@ static void zend_fiber_object_free(zend_object *object)
445445
zend_object_std_dtor(&fiber->std);
446446
}
447447

448+
ZEND_API zend_fiber *zend_fiber_create(const zend_fcall_info *fci, const zend_fcall_info_cache *fci_cache)
449+
{
450+
zend_fiber *fiber = (zend_fiber *) zend_fiber_object_create(zend_ce_fiber);
451+
452+
fiber->fci = *fci;
453+
fiber->fci_cache = *fci_cache;
454+
455+
Z_TRY_ADDREF(fiber->fci.function_name);
456+
457+
return fiber;
458+
}
459+
448460
ZEND_METHOD(Fiber, __construct)
449461
{
450462
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis());
@@ -457,17 +469,8 @@ ZEND_METHOD(Fiber, __construct)
457469
Z_TRY_ADDREF(fiber->fci.function_name);
458470
}
459471

460-
ZEND_METHOD(Fiber, start)
472+
ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_count, zend_array *named_params, zval *return_value)
461473
{
462-
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis());
463-
zval *params;
464-
uint32_t param_count;
465-
zend_array *named_params;
466-
467-
ZEND_PARSE_PARAMETERS_START(0, -1)
468-
Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params);
469-
ZEND_PARSE_PARAMETERS_END();
470-
471474
if (fiber->status != ZEND_FIBER_STATUS_INIT) {
472475
zend_throw_error(zend_ce_fiber_error, "Cannot start a fiber that has already been started");
473476
RETURN_THROWS();
@@ -492,16 +495,24 @@ ZEND_METHOD(Fiber, start)
492495
ZVAL_UNDEF(&fiber->value);
493496
}
494497

495-
ZEND_METHOD(Fiber, suspend)
498+
ZEND_METHOD(Fiber, start)
496499
{
497-
zend_fiber *fiber = EG(current_fiber);
498-
zval *exception, *value = NULL;
500+
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(getThis());
501+
zval *params;
502+
uint32_t param_count;
503+
zend_array *named_params;
499504

500-
ZEND_PARSE_PARAMETERS_START(0, 1)
501-
Z_PARAM_OPTIONAL
502-
Z_PARAM_ZVAL(value);
505+
ZEND_PARSE_PARAMETERS_START(0, -1)
506+
Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params);
503507
ZEND_PARSE_PARAMETERS_END();
504508

509+
zend_fiber_start(fiber, params, param_count, named_params, return_value);
510+
}
511+
512+
ZEND_API void zend_fiber_suspend(zval *value, zval *return_value)
513+
{
514+
zend_fiber *fiber = EG(current_fiber);
515+
505516
if (UNEXPECTED(!fiber)) {
506517
zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber");
507518
RETURN_THROWS();
@@ -520,11 +531,11 @@ ZEND_METHOD(Fiber, suspend)
520531
ZVAL_NULL(&fiber->value);
521532
}
522533

523-
fiber->execute_data = execute_data;
534+
fiber->execute_data = EG(current_execute_data);
524535
fiber->status = ZEND_FIBER_STATUS_SUSPENDED;
525536
fiber->stack_bottom->prev_execute_data = NULL;
526537

527-
zend_fiber_suspend(fiber);
538+
zend_fiber_suspend_from(fiber);
528539

529540
if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) {
530541
// This occurs when the fiber is GC'ed while suspended.
@@ -535,7 +546,7 @@ ZEND_METHOD(Fiber, suspend)
535546
fiber->status = ZEND_FIBER_STATUS_RUNNING;
536547

537548
if (fiber->exception) {
538-
exception = fiber->exception;
549+
zval *exception = fiber->exception;
539550
fiber->exception = NULL;
540551

541552
zend_throw_exception_object(exception);
@@ -546,18 +557,20 @@ ZEND_METHOD(Fiber, suspend)
546557
ZVAL_UNDEF(&fiber->value);
547558
}
548559

549-
ZEND_METHOD(Fiber, resume)
560+
ZEND_METHOD(Fiber, suspend)
550561
{
551-
zend_fiber *fiber;
552562
zval *value = NULL;
553563

554564
ZEND_PARSE_PARAMETERS_START(0, 1)
555565
Z_PARAM_OPTIONAL
556566
Z_PARAM_ZVAL(value);
557567
ZEND_PARSE_PARAMETERS_END();
558568

559-
fiber = (zend_fiber *) Z_OBJ_P(getThis());
569+
zend_fiber_suspend(value, return_value);
570+
}
560571

572+
ZEND_API void zend_fiber_resume(zend_fiber *fiber, zval *value, zval *return_value)
573+
{
561574
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) {
562575
zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended");
563576
RETURN_THROWS();
@@ -570,7 +583,7 @@ ZEND_METHOD(Fiber, resume)
570583
}
571584

572585
fiber->status = ZEND_FIBER_STATUS_RUNNING;
573-
fiber->stack_bottom->prev_execute_data = execute_data;
586+
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
574587

575588
zend_fiber_switch_to(fiber);
576589

@@ -582,17 +595,23 @@ ZEND_METHOD(Fiber, resume)
582595
ZVAL_UNDEF(&fiber->value);
583596
}
584597

585-
ZEND_METHOD(Fiber, throw)
598+
ZEND_METHOD(Fiber, resume)
586599
{
587600
zend_fiber *fiber;
588-
zval *exception;
601+
zval *value = NULL;
589602

590-
ZEND_PARSE_PARAMETERS_START(1, 1)
591-
Z_PARAM_OBJECT_OF_CLASS(exception, zend_ce_throwable)
603+
ZEND_PARSE_PARAMETERS_START(0, 1)
604+
Z_PARAM_OPTIONAL
605+
Z_PARAM_ZVAL(value);
592606
ZEND_PARSE_PARAMETERS_END();
593607

594608
fiber = (zend_fiber *) Z_OBJ_P(getThis());
595609

610+
zend_fiber_resume(fiber, value, return_value);
611+
}
612+
613+
ZEND_API void zend_fiber_throw(zend_fiber *fiber, zval *exception, zval *return_value)
614+
{
596615
if (UNEXPECTED(fiber->status != ZEND_FIBER_STATUS_SUSPENDED)) {
597616
zend_throw_error(zend_ce_fiber_error, "Cannot resume a fiber that is not suspended");
598617
RETURN_THROWS();
@@ -602,7 +621,7 @@ ZEND_METHOD(Fiber, throw)
602621
fiber->exception = exception;
603622

604623
fiber->status = ZEND_FIBER_STATUS_RUNNING;
605-
fiber->stack_bottom->prev_execute_data = execute_data;
624+
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
606625

607626
zend_fiber_switch_to(fiber);
608627

@@ -614,6 +633,20 @@ ZEND_METHOD(Fiber, throw)
614633
ZVAL_UNDEF(&fiber->value);
615634
}
616635

636+
ZEND_METHOD(Fiber, throw)
637+
{
638+
zend_fiber *fiber;
639+
zval *exception;
640+
641+
ZEND_PARSE_PARAMETERS_START(1, 1)
642+
Z_PARAM_OBJECT_OF_CLASS(exception, zend_ce_throwable)
643+
ZEND_PARSE_PARAMETERS_END();
644+
645+
fiber = (zend_fiber *) Z_OBJ_P(getThis());
646+
647+
zend_fiber_throw(fiber, exception, return_value);
648+
}
649+
617650
ZEND_METHOD(Fiber, isStarted)
618651
{
619652
zend_fiber *fiber;

Zend/zend_fibers.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,16 @@ static const zend_uchar ZEND_FIBER_STATUS_BAILOUT = 0x20;
9292

9393
static const zend_uchar ZEND_FIBER_STATUS_FINISHED = 0x2c;
9494

95+
/* These functions create and manipulate a Fiber object, allowing any internal function to start, resume, or suspend a fiber. */
96+
ZEND_API zend_fiber *zend_fiber_create(const zend_fcall_info *fci, const zend_fcall_info_cache *fci_cache);
97+
ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_count, zend_array *named_params, zval *return_value);
98+
ZEND_API void zend_fiber_suspend(zval *value, zval *return_value);
99+
ZEND_API void zend_fiber_resume(zend_fiber *fiber, zval *value, zval *return_value);
100+
ZEND_API void zend_fiber_throw(zend_fiber *fiber, zval *exception, zval *return_value);
101+
102+
/* These functions may be used to create custom fibers (coroutines) using the bundled fiber switching context. */
95103
ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size);
96104
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context);
97-
98105
ZEND_API void zend_fiber_switch_context(zend_fiber_context *to);
99106
ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current);
100107

0 commit comments

Comments
 (0)