Skip to content

Commit c5f9cde

Browse files
authored
Flexible fiber bailout handling (#7163)
1 parent e9e0627 commit c5f9cde

File tree

5 files changed

+48
-23
lines changed

5 files changed

+48
-23
lines changed

Zend/zend_fibers.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
343343

344344
to->status = ZEND_FIBER_STATUS_RUNNING;
345345

346-
if (from->status == ZEND_FIBER_STATUS_RUNNING) {
346+
if (EXPECTED(from->status == ZEND_FIBER_STATUS_RUNNING)) {
347347
from->status = ZEND_FIBER_STATUS_SUSPENDED;
348348
}
349349

@@ -381,11 +381,6 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
381381
if (to->status == ZEND_FIBER_STATUS_DEAD) {
382382
zend_fiber_destroy_context(to);
383383
}
384-
385-
/* Propagate bailout to current fiber / main. */
386-
if (UNEXPECTED(to->flags & ZEND_FIBER_FLAG_BAILOUT)) {
387-
zend_bailout();
388-
}
389384
}
390385

391386
static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
@@ -400,6 +395,7 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
400395
}
401396

402397
EG(vm_stack) = NULL;
398+
transfer->flags = 0;
403399

404400
zend_first_try {
405401
zend_vm_stack stack = zend_vm_stack_new_page(ZEND_FIBER_VM_STACK_SIZE, NULL);
@@ -427,10 +423,10 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
427423
zval_ptr_dtor(&fiber->fci.function_name);
428424

429425
if (EG(exception)) {
430-
if (!(fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED)
426+
if (!(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)
431427
|| !(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))
432428
) {
433-
fiber->context.flags |= ZEND_FIBER_FLAG_THREW;
429+
fiber->flags |= ZEND_FIBER_FLAG_THREW;
434430
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_ERROR;
435431

436432
ZVAL_OBJ_COPY(&transfer->value, EG(exception));
@@ -441,7 +437,8 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
441437
ZVAL_COPY(&transfer->value, &fiber->result);
442438
}
443439
} zend_catch {
444-
fiber->context.flags |= ZEND_FIBER_FLAG_BAILOUT;
440+
fiber->flags |= ZEND_FIBER_FLAG_BAILOUT;
441+
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_BAILOUT;
445442
} zend_end_try();
446443

447444
transfer->context = fiber->caller;
@@ -486,6 +483,11 @@ static zend_always_inline zend_fiber_transfer zend_fiber_switch_to(
486483

487484
zend_fiber_switch_context(&transfer);
488485

486+
/* Forward bailout into current fiber. */
487+
if (UNEXPECTED(transfer.flags & ZEND_FIBER_TRANSFER_FLAG_BAILOUT)) {
488+
zend_bailout();
489+
}
490+
489491
return transfer;
490492
}
491493

@@ -538,7 +540,7 @@ static void zend_fiber_object_destroy(zend_object *object)
538540
zend_object *exception = EG(exception);
539541
EG(exception) = NULL;
540542

541-
fiber->context.flags |= ZEND_FIBER_FLAG_DESTROYED;
543+
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
542544

543545
zend_fiber_transfer transfer = zend_fiber_resume(fiber, NULL, false);
544546

@@ -639,7 +641,7 @@ ZEND_METHOD(Fiber, suspend)
639641
RETURN_THROWS();
640642
}
641643

642-
if (UNEXPECTED(fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED)) {
644+
if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)) {
643645
zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force-closed fiber");
644646
RETURN_THROWS();
645647
}
@@ -656,7 +658,7 @@ ZEND_METHOD(Fiber, suspend)
656658

657659
zend_fiber_transfer transfer = zend_fiber_suspend(fiber, value);
658660

659-
if (fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED) {
661+
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
660662
// This occurs when the fiber is GC'ed while suspended.
661663
zval_ptr_dtor(&transfer.value);
662664
zend_throw_graceful_exit();
@@ -777,9 +779,9 @@ ZEND_METHOD(Fiber, getReturn)
777779
fiber = (zend_fiber *) Z_OBJ_P(getThis());
778780

779781
if (fiber->context.status == ZEND_FIBER_STATUS_DEAD) {
780-
if (fiber->context.flags & ZEND_FIBER_FLAG_THREW) {
782+
if (fiber->flags & ZEND_FIBER_FLAG_THREW) {
781783
message = "The fiber threw an exception";
782-
} else if (fiber->context.flags & ZEND_FIBER_FLAG_BAILOUT) {
784+
} else if (fiber->flags & ZEND_FIBER_FLAG_BAILOUT) {
783785
message = "The fiber exited with a fatal error";
784786
} else {
785787
RETURN_COPY(&fiber->result);

Zend/zend_fibers.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ typedef enum {
4545

4646
typedef enum {
4747
ZEND_FIBER_TRANSFER_FLAG_ERROR = 1 << 0,
48+
ZEND_FIBER_TRANSFER_FLAG_BAILOUT = 1 << 1
4849
} zend_fiber_transfer_flag;
4950

5051
void zend_register_fiber_ce(void);
@@ -61,8 +62,10 @@ typedef struct _zend_fiber_stack zend_fiber_stack;
6162
typedef struct _zend_fiber_transfer {
6263
/* Fiber that will be switched to / has resumed us. */
6364
zend_fiber_context *context;
65+
6466
/* Value to that should be send to (or was received from) a fiber. */
6567
zval value;
68+
6669
/* Bitmask of flags defined in enum zend_fiber_transfer_flag. */
6770
uint8_t flags;
6871
} zend_fiber_transfer;
@@ -74,18 +77,28 @@ typedef void (*zend_fiber_coroutine)(zend_fiber_transfer *transfer);
7477
struct _zend_fiber_context {
7578
/* Handle to fiber state as needed by boost.context */
7679
void *handle;
80+
7781
/* Pointer that identifies the fiber type. */
7882
void *kind;
83+
84+
/* Entrypoint function of the fiber. */
7985
zend_fiber_coroutine function;
86+
87+
/* Assigned C stack. */
8088
zend_fiber_stack *stack;
89+
90+
/* Fiber status. */
8191
zend_fiber_status status;
82-
uint8_t flags;
8392
};
8493

8594
struct _zend_fiber {
8695
/* PHP object handle. */
8796
zend_object std;
8897

98+
/* Flags are defined in enum zend_fiber_flag. */
99+
uint8_t flags;
100+
101+
/* Native C fiber context. */
89102
zend_fiber_context context;
90103

91104
/* Fiber that resumed us. */

ext/zend_test/fiber.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ static zend_fiber_transfer zend_test_fiber_switch_to(zend_fiber_context *context
3838

3939
zend_fiber_switch_context(&transfer);
4040

41+
/* Forward bailout into current fiber. */
42+
if (UNEXPECTED(transfer.flags & ZEND_FIBER_TRANSFER_FLAG_BAILOUT)) {
43+
zend_bailout();
44+
}
45+
4146
return transfer;
4247
}
4348

@@ -74,6 +79,7 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran
7479
zend_execute_data *execute_data;
7580

7681
EG(vm_stack) = NULL;
82+
transfer->flags = 0;
7783

7884
zend_first_try {
7985
zend_vm_stack stack = zend_vm_stack_new_page(ZEND_FIBER_VM_STACK_SIZE, NULL);
@@ -97,10 +103,10 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran
97103
zval_ptr_dtor(&fiber->fci.function_name);
98104

99105
if (EG(exception)) {
100-
if (!(fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED)
106+
if (!(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)
101107
|| !(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))
102108
) {
103-
fiber->context.flags |= ZEND_FIBER_FLAG_THREW;
109+
fiber->flags |= ZEND_FIBER_FLAG_THREW;
104110
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_ERROR;
105111

106112
ZVAL_OBJ_COPY(&transfer->value, EG(exception));
@@ -112,7 +118,8 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran
112118
ZVAL_COPY(&transfer->value, &fiber->result);
113119
}
114120
} zend_catch {
115-
fiber->context.flags |= ZEND_FIBER_FLAG_BAILOUT;
121+
fiber->flags |= ZEND_FIBER_FLAG_BAILOUT;
122+
transfer->flags = ZEND_FIBER_TRANSFER_FLAG_BAILOUT;
116123
} zend_end_try();
117124

118125
zend_vm_stack_destroy();
@@ -159,7 +166,7 @@ static void zend_test_fiber_object_destroy(zend_object *object)
159166
zend_object *exception = EG(exception);
160167
EG(exception) = NULL;
161168

162-
fiber->context.flags |= ZEND_FIBER_FLAG_DESTROYED;
169+
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
163170

164171
zend_fiber_transfer transfer = zend_test_fiber_resume(fiber, NULL, false);
165172

@@ -274,7 +281,7 @@ static ZEND_METHOD(_ZendTestFiber, suspend)
274281

275282
zend_fiber_transfer transfer = zend_test_fiber_suspend(fiber, value);
276283

277-
if (fiber->context.flags & ZEND_FIBER_FLAG_DESTROYED) {
284+
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
278285
// This occurs when the test fiber is GC'ed while suspended.
279286
zval_ptr_dtor(&transfer.value);
280287
zend_throw_graceful_exit();

ext/zend_test/fiber.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typedef struct _zend_test_fiber zend_test_fiber;
2323

2424
struct _zend_test_fiber {
2525
zend_object std;
26+
uint8_t flags;
2627
zend_fiber_context context;
2728
zend_fiber_context *caller;
2829
zend_fiber_context *previous;

ext/zend_test/observer.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ static void fiber_enter_observer(zend_fiber_context *from, zend_fiber_context *t
207207
return;
208208
}
209209

210-
if (to->flags & ZEND_FIBER_FLAG_DESTROYED) {
210+
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
211211
php_printf("<destroying '%p'>\n", to);
212212
} else if (to->status != ZEND_FIBER_STATUS_DEAD) {
213213
php_printf("<resume '%p'>\n", to);
@@ -220,9 +220,11 @@ static void fiber_suspend_observer(zend_fiber_context *from, zend_fiber_context
220220
{
221221
if (ZT_G(observer_fiber_switch)) {
222222
if (from->status == ZEND_FIBER_STATUS_DEAD) {
223-
if (from->flags & ZEND_FIBER_FLAG_THREW) {
223+
zend_fiber *fiber = (from->kind == zend_ce_fiber) ? zend_fiber_from_context(from) : NULL;
224+
225+
if (fiber && fiber->flags & ZEND_FIBER_FLAG_THREW) {
224226
php_printf("<threw '%p'>\n", from);
225-
} else if (from->flags & ZEND_FIBER_FLAG_DESTROYED) {
227+
} else if (fiber && fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
226228
php_printf("<destroyed '%p'>\n", from);
227229
} else {
228230
php_printf("<returned '%p'>\n", from);

0 commit comments

Comments
 (0)