Skip to content

Commit e6e6b3e

Browse files
authored
Improve fiber interoperability (#7128)
1 parent 16feae5 commit e6e6b3e

File tree

5 files changed

+87
-72
lines changed

5 files changed

+87
-72
lines changed

Zend/zend.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,9 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
774774
executor_globals->exception_class = NULL;
775775
executor_globals->exception = NULL;
776776
executor_globals->objects_store.object_buckets = NULL;
777-
executor_globals->current_fiber = NULL;
777+
executor_globals->current_fiber_context = NULL;
778+
executor_globals->main_fiber_context = NULL;
779+
executor_globals->active_fiber = NULL;
778780
#ifdef ZEND_WIN32
779781
zend_get_windows_version_info(&executor_globals->windows_version_info);
780782
#endif

Zend/zend_fibers.c

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ static ZEND_NORETURN void zend_fiber_trampoline(boost_context_data data)
204204
/* Initialize transfer struct with a copy of passed data. */
205205
zend_fiber_transfer transfer = *data.transfer;
206206

207-
zend_fiber_context *context = EG(current_fiber);
207+
zend_fiber_context *context = EG(current_fiber_context);
208208

209209
context->function(&transfer);
210210
context->status = ZEND_FIBER_STATUS_DEAD;
@@ -241,7 +241,7 @@ ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
241241

242242
ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
243243
{
244-
zend_fiber_context *from = EG(current_fiber);
244+
zend_fiber_context *from = EG(current_fiber_context);
245245
zend_fiber_context *to = transfer->context;
246246
zend_fiber_vm_state state;
247247

@@ -272,7 +272,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
272272
/* Update transfer context with the current fiber before switching. */
273273
transfer->context = from;
274274

275-
EG(current_fiber) = to;
275+
EG(current_fiber_context) = to;
276276

277277
#ifdef __SANITIZE_ADDRESS__
278278
void *fake_stack = NULL;
@@ -295,7 +295,7 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
295295
__sanitizer_finish_switch_fiber(fake_stack, &to->stack.prior_pointer, &to->stack.prior_size);
296296
#endif
297297

298-
EG(current_fiber) = from;
298+
EG(current_fiber_context) = from;
299299

300300
zend_fiber_restore_vm_state(&state);
301301

@@ -312,11 +312,11 @@ ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
312312

313313
static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
314314
{
315-
ZEND_ASSERT(Z_TYPE(transfer->value) == IS_UNDEF && "First context switch into a fiber must not transmit data");
316-
317-
zend_fiber *fiber = zend_fiber_from_context(EG(current_fiber));
315+
zend_fiber *fiber = EG(active_fiber);
318316

317+
/* Determine the current error_reporting ini setting. */
319318
zend_long error_reporting = INI_INT("error_reporting");
319+
/* If error_reporting is 0 and not explicitly set to 0, INI_STR returns a null pointer. */
320320
if (!error_reporting && !INI_STR("error_reporting")) {
321321
error_reporting = E_ALL;
322322
}
@@ -392,6 +392,50 @@ static zend_always_inline void delegate_transfer_result(
392392
RETURN_COPY_VALUE(&transfer->value);
393393
}
394394

395+
static zend_always_inline zend_fiber_transfer zend_fiber_switch_to(
396+
zend_fiber_context *context, zval *value, bool exception
397+
) {
398+
zend_fiber_transfer transfer = {
399+
.context = context,
400+
.flags = exception ? ZEND_FIBER_TRANSFER_FLAG_ERROR : 0,
401+
};
402+
403+
if (value) {
404+
ZVAL_COPY(&transfer.value, value);
405+
} else {
406+
ZVAL_NULL(&transfer.value);
407+
}
408+
409+
zend_fiber_switch_context(&transfer);
410+
411+
return transfer;
412+
}
413+
414+
static zend_always_inline zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception)
415+
{
416+
zend_fiber *previous = EG(active_fiber);
417+
418+
fiber->caller = EG(current_fiber_context);
419+
EG(active_fiber) = fiber;
420+
421+
zend_fiber_transfer transfer = zend_fiber_switch_to(fiber->previous, value, exception);
422+
423+
EG(active_fiber) = previous;
424+
425+
return transfer;
426+
}
427+
428+
static zend_always_inline zend_fiber_transfer zend_fiber_suspend(zend_fiber *fiber, zval *value)
429+
{
430+
ZEND_ASSERT(fiber->caller != NULL);
431+
432+
zend_fiber_context *caller = fiber->caller;
433+
fiber->previous = EG(current_fiber_context);
434+
fiber->caller = NULL;
435+
436+
return zend_fiber_switch_to(caller, value, false);
437+
}
438+
395439
static zend_object *zend_fiber_object_create(zend_class_entry *ce)
396440
{
397441
zend_fiber *fiber;
@@ -416,14 +460,9 @@ static void zend_fiber_object_destroy(zend_object *object)
416460
zend_object *exception = EG(exception);
417461
EG(exception) = NULL;
418462

419-
fiber->caller = EG(current_fiber);
420463
fiber->flags |= ZEND_FIBER_FLAG_DESTROYED;
421464

422-
zend_fiber_transfer transfer = {
423-
.context = zend_fiber_get_context(fiber)
424-
};
425-
426-
zend_fiber_switch_context(&transfer);
465+
zend_fiber_transfer transfer = zend_fiber_resume(fiber, NULL, false);
427466

428467
if (transfer.flags & ZEND_FIBER_TRANSFER_FLAG_ERROR) {
429468
EG(exception) = Z_OBJ(transfer.value);
@@ -496,13 +535,9 @@ ZEND_METHOD(Fiber, start)
496535
RETURN_THROWS();
497536
}
498537

499-
fiber->caller = EG(current_fiber);
500-
501-
zend_fiber_transfer transfer = {
502-
.context = context
503-
};
538+
fiber->previous = context;
504539

505-
zend_fiber_switch_context(&transfer);
540+
zend_fiber_transfer transfer = zend_fiber_resume(fiber, NULL, false);
506541

507542
delegate_transfer_result(fiber, &transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU);
508543
}
@@ -516,38 +551,24 @@ ZEND_METHOD(Fiber, suspend)
516551
Z_PARAM_ZVAL(value);
517552
ZEND_PARSE_PARAMETERS_END();
518553

519-
if (UNEXPECTED(EG(current_fiber)->kind != zend_ce_fiber)) {
554+
zend_fiber *fiber = EG(active_fiber);
555+
556+
if (UNEXPECTED(!fiber)) {
520557
zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber");
521558
RETURN_THROWS();
522559
}
523560

524-
zend_fiber *fiber = zend_fiber_from_context(EG(current_fiber));
525-
zend_fiber_context *caller = fiber->caller;
526-
527561
if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)) {
528562
zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force-closed fiber");
529563
RETURN_THROWS();
530564
}
531565

532-
ZEND_ASSERT(fiber->status == ZEND_FIBER_STATUS_RUNNING);
533-
ZEND_ASSERT(caller != NULL);
534-
535-
fiber->caller = NULL;
566+
ZEND_ASSERT(fiber->status == ZEND_FIBER_STATUS_RUNNING || fiber->status == ZEND_FIBER_STATUS_SUSPENDED);
536567

537568
fiber->execute_data = EG(current_execute_data);
538569
fiber->stack_bottom->prev_execute_data = NULL;
539570

540-
zend_fiber_transfer transfer = {
541-
.context = caller
542-
};
543-
544-
if (value) {
545-
ZVAL_COPY(&transfer.value, value);
546-
} else {
547-
ZVAL_NULL(&transfer.value);
548-
}
549-
550-
zend_fiber_switch_context(&transfer);
571+
zend_fiber_transfer transfer = zend_fiber_suspend(fiber, value);
551572

552573
if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
553574
// This occurs when the fiber is GC'ed while suspended.
@@ -576,21 +597,9 @@ ZEND_METHOD(Fiber, resume)
576597
RETURN_THROWS();
577598
}
578599

579-
fiber->caller = EG(current_fiber);
580-
581600
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
582601

583-
zend_fiber_transfer transfer = {
584-
.context = zend_fiber_get_context(fiber)
585-
};
586-
587-
if (value) {
588-
ZVAL_COPY(&transfer.value, value);
589-
} else {
590-
ZVAL_NULL(&transfer.value);
591-
}
592-
593-
zend_fiber_switch_context(&transfer);
602+
zend_fiber_transfer transfer = zend_fiber_resume(fiber, value, false);
594603

595604
delegate_transfer_result(fiber, &transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU);
596605
}
@@ -611,18 +620,9 @@ ZEND_METHOD(Fiber, throw)
611620
RETURN_THROWS();
612621
}
613622

614-
fiber->caller = EG(current_fiber);
615-
616623
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
617624

618-
zend_fiber_transfer transfer = {
619-
.context = zend_fiber_get_context(fiber),
620-
.flags = ZEND_FIBER_TRANSFER_FLAG_ERROR
621-
};
622-
623-
ZVAL_COPY(&transfer.value, exception);
624-
625-
zend_fiber_switch_context(&transfer);
625+
zend_fiber_transfer transfer = zend_fiber_resume(fiber, exception, true);
626626

627627
delegate_transfer_result(fiber, &transfer, INTERNAL_FUNCTION_PARAM_PASSTHRU);
628628
}
@@ -702,11 +702,13 @@ ZEND_METHOD(Fiber, this)
702702
{
703703
ZEND_PARSE_PARAMETERS_NONE();
704704

705-
if (EG(current_fiber)->kind != zend_ce_fiber) {
705+
zend_fiber *fiber = EG(active_fiber);
706+
707+
if (!fiber) {
706708
RETURN_NULL();
707709
}
708710

709-
RETURN_OBJ_COPY(&zend_fiber_from_context(EG(current_fiber))->std);
711+
RETURN_OBJ_COPY(&fiber->std);
710712
}
711713

712714
ZEND_METHOD(FiberError, __construct)
@@ -741,11 +743,12 @@ void zend_fiber_init(void)
741743

742744
context->status = ZEND_FIBER_STATUS_RUNNING;
743745

744-
EG(main_fiber) = context;
745-
EG(current_fiber) = context;
746+
EG(main_fiber_context) = context;
747+
EG(current_fiber_context) = context;
748+
EG(active_fiber) = NULL;
746749
}
747750

748751
void zend_fiber_shutdown(void)
749752
{
750-
efree(EG(main_fiber));
753+
efree(EG(main_fiber_context));
751754
}

Zend/zend_fibers.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ typedef struct _zend_fiber_vm_state {
111111
int error_reporting;
112112
uint32_t jit_trace_num;
113113
JMP_BUF *bailout;
114+
zend_fiber *active_fiber;
114115
} zend_fiber_vm_state;
115116

116117
struct _zend_fiber {
@@ -123,6 +124,9 @@ struct _zend_fiber {
123124
/* Fiber that resumed us. */
124125
zend_fiber_context *caller;
125126

127+
/* Fiber that suspended us. */
128+
zend_fiber_context *previous;
129+
126130
/* Callback and info / cache to be used when fiber is started. */
127131
zend_fcall_info fci;
128132
zend_fcall_info_cache fci_cache;
@@ -166,6 +170,7 @@ static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state *
166170
state->error_reporting = EG(error_reporting);
167171
state->jit_trace_num = EG(jit_trace_num);
168172
state->bailout = EG(bailout);
173+
state->active_fiber = EG(active_fiber);
169174
}
170175

171176
static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *state)
@@ -178,6 +183,7 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *
178183
EG(error_reporting) = state->error_reporting;
179184
EG(jit_trace_num) = state->jit_trace_num;
180185
EG(bailout) = state->bailout;
186+
EG(active_fiber) = state->active_fiber;
181187
}
182188

183189
#endif

Zend/zend_globals.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ END_EXTERN_C()
6262
typedef struct _zend_vm_stack *zend_vm_stack;
6363
typedef struct _zend_ini_entry zend_ini_entry;
6464
typedef struct _zend_fiber_context zend_fiber_context;
65+
typedef struct _zend_fiber zend_fiber;
6566

6667
struct _zend_compiler_globals {
6768
zend_stack loop_var_stack;
@@ -249,8 +250,11 @@ struct _zend_executor_globals {
249250

250251
zend_get_gc_buffer get_gc_buffer;
251252

252-
zend_fiber_context *main_fiber;
253-
zend_fiber_context *current_fiber;
253+
zend_fiber_context *main_fiber_context;
254+
zend_fiber_context *current_fiber_context;
255+
256+
/* Active instance of Fiber. */
257+
zend_fiber *active_fiber;
254258

255259
/* Default fiber C stack size. */
256260
zend_long fiber_stack_size;

ext/reflection/php_reflection.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6895,7 +6895,7 @@ ZEND_METHOD(ReflectionFiber, getTrace)
68956895
prev_execute_data = fiber->stack_bottom->prev_execute_data;
68966896
fiber->stack_bottom->prev_execute_data = NULL;
68976897

6898-
if (EG(current_fiber) != zend_fiber_get_context(fiber)) {
6898+
if (EG(active_fiber) != fiber) {
68996899
// No need to replace current execute data if within the current fiber.
69006900
EG(current_execute_data) = fiber->execute_data;
69016901
}
@@ -6915,7 +6915,7 @@ ZEND_METHOD(ReflectionFiber, getExecutingLine)
69156915

69166916
REFLECTION_CHECK_VALID_FIBER(fiber);
69176917

6918-
if (EG(current_fiber) == zend_fiber_get_context(fiber)) {
6918+
if (EG(active_fiber) == fiber) {
69196919
prev_execute_data = execute_data->prev_execute_data;
69206920
} else {
69216921
prev_execute_data = fiber->execute_data->prev_execute_data;
@@ -6933,7 +6933,7 @@ ZEND_METHOD(ReflectionFiber, getExecutingFile)
69336933

69346934
REFLECTION_CHECK_VALID_FIBER(fiber);
69356935

6936-
if (EG(current_fiber) == zend_fiber_get_context(fiber)) {
6936+
if (EG(active_fiber) == fiber) {
69376937
prev_execute_data = execute_data->prev_execute_data;
69386938
} else {
69396939
prev_execute_data = fiber->execute_data->prev_execute_data;

0 commit comments

Comments
 (0)