Skip to content

Commit a65989b

Browse files
authored
Alternative Fiber Internals Refactoring (#7101)
1 parent 0dbbf9e commit a65989b

14 files changed

+202
-194
lines changed

Zend/zend_execute_API.c

+1
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ void shutdown_executor(void) /* {{{ */
380380
zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown);
381381

382382
zend_weakrefs_shutdown();
383+
zend_fiber_shutdown();
383384

384385
zend_try {
385386
zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator);

Zend/zend_fibers.c

+65-102
Original file line numberDiff line numberDiff line change
@@ -78,28 +78,6 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp);
7878

7979
#define ZEND_FIBER_DEFAULT_PAGE_SIZE 4096
8080

81-
#define ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, trace_num, bailout) do { \
82-
stack = EG(vm_stack); \
83-
stack->top = EG(vm_stack_top); \
84-
stack->end = EG(vm_stack_end); \
85-
stack_page_size = EG(vm_stack_page_size); \
86-
execute_data = EG(current_execute_data); \
87-
error_reporting = EG(error_reporting); \
88-
trace_num = EG(jit_trace_num); \
89-
bailout = EG(bailout); \
90-
} while (0)
91-
92-
#define ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, trace_num, bailout) do { \
93-
EG(vm_stack) = stack; \
94-
EG(vm_stack_top) = stack->top; \
95-
EG(vm_stack_end) = stack->end; \
96-
EG(vm_stack_page_size) = stack_page_size; \
97-
EG(current_execute_data) = execute_data; \
98-
EG(error_reporting) = error_reporting; \
99-
EG(jit_trace_num) = trace_num; \
100-
EG(bailout) = bailout; \
101-
} while (0)
102-
10381
static size_t zend_fiber_get_page_size(void)
10482
{
10583
static size_t page_size = 0;
@@ -172,6 +150,11 @@ static bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size)
172150
stack->valgrind = VALGRIND_STACK_REGISTER(base, base + stack->size);
173151
#endif
174152

153+
#ifdef __SANITIZE_ADDRESS__
154+
stack->prior_pointer = stack->pointer;
155+
stack->prior_size = stack->size;
156+
#endif
157+
175158
return true;
176159
}
177160

@@ -200,24 +183,19 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack)
200183

201184
static ZEND_NORETURN void zend_fiber_trampoline(transfer_t transfer)
202185
{
203-
zend_fiber_context *context = transfer.data;
204-
205-
#ifdef __SANITIZE_ADDRESS__
206-
__sanitizer_finish_switch_fiber(NULL, &context->stack.prior_pointer, &context->stack.prior_size);
207-
#endif
208-
209-
context->caller = transfer.context;
210-
211-
context->function(context);
186+
zend_fiber_context *context = EG(current_fiber);
187+
zend_fiber_context *from = transfer.data;
212188

213-
context->self = NULL;
189+
from->handle = transfer.context;
214190

215191
#ifdef __SANITIZE_ADDRESS__
216-
__sanitizer_start_switch_fiber(NULL, context->stack.prior_pointer, context->stack.prior_size);
192+
__sanitizer_finish_switch_fiber(NULL, &from->stack.prior_pointer, &from->stack.prior_size);
217193
#endif
218194

219-
jump_fcontext(context->caller, NULL);
195+
/* Final context switch, the fiber must not be resumed afterwards! */
196+
zend_fiber_switch_context(context->function(context));
220197

198+
/* Abort here because we are in an inconsistent program state. */
221199
abort();
222200
}
223201

@@ -230,10 +208,10 @@ ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_co
230208
// Stack grows down, calculate the top of the stack. make_fcontext then shifts pointer to lower 16-byte boundary.
231209
void *stack = (void *) ((uintptr_t) context->stack.pointer + context->stack.size);
232210

233-
context->self = make_fcontext(stack, context->stack.size, zend_fiber_trampoline);
234-
ZEND_ASSERT(context->self != NULL && "make_fcontext() never returns NULL");
211+
context->handle = make_fcontext(stack, context->stack.size, zend_fiber_trampoline);
212+
ZEND_ASSERT(context->handle != NULL && "make_fcontext() never returns NULL");
213+
235214
context->function = coroutine;
236-
context->caller = NULL;
237215

238216
return true;
239217
}
@@ -245,81 +223,59 @@ ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
245223

246224
ZEND_API void zend_fiber_switch_context(zend_fiber_context *to)
247225
{
248-
ZEND_ASSERT(to && to->self && to->stack.pointer && "Invalid fiber context");
226+
zend_fiber_context *from = EG(current_fiber);
249227

250-
#ifdef __SANITIZE_ADDRESS__
251-
void *fake_stack;
252-
__sanitizer_start_switch_fiber(&fake_stack, to->stack.pointer, to->stack.size);
253-
#endif
254-
255-
transfer_t transfer = jump_fcontext(to->self, to);
228+
ZEND_ASSERT(to && to->handle && "Invalid fiber context");
229+
ZEND_ASSERT(from && "From fiber context must be present");
230+
ZEND_ASSERT(to != from && "Cannot switch into the running fiber context");
256231

257232
#ifdef __SANITIZE_ADDRESS__
258-
__sanitizer_finish_switch_fiber(fake_stack, &to->stack.prior_pointer, &to->stack.prior_size);
233+
void *fake_stack = NULL;
234+
__sanitizer_start_switch_fiber(
235+
from->status != ZEND_FIBER_STATUS_DEAD ? &fake_stack : NULL,
236+
to->stack.prior_pointer,
237+
to->stack.prior_size);
259238
#endif
260239

261-
to->self = transfer.context;
262-
}
263-
264-
ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current)
265-
{
266-
ZEND_ASSERT(current && current->caller && current->stack.pointer && "Invalid fiber context");
240+
EG(current_fiber) = to;
267241

268-
#ifdef __SANITIZE_ADDRESS__
269-
void *fake_stack;
270-
__sanitizer_start_switch_fiber(&fake_stack, current->stack.prior_pointer, current->stack.prior_size);
271-
#endif
242+
transfer_t transfer = jump_fcontext(to->handle, from);
243+
((zend_fiber_context *) transfer.data)->handle = transfer.context;
272244

273-
transfer_t transfer = jump_fcontext(current->caller, NULL);
245+
EG(current_fiber) = from;
274246

275247
#ifdef __SANITIZE_ADDRESS__
276-
__sanitizer_finish_switch_fiber(fake_stack, &current->stack.prior_pointer, &current->stack.prior_size);
248+
__sanitizer_finish_switch_fiber(fake_stack, &to->stack.prior_pointer, &to->stack.prior_size);
277249
#endif
278-
279-
current->caller = transfer.context;
280250
}
281251

282252
static void zend_fiber_suspend_from(zend_fiber *fiber)
283253
{
284-
zend_vm_stack stack;
285-
size_t stack_page_size;
286-
zend_execute_data *execute_data;
287-
int error_reporting;
288-
uint32_t jit_trace_num;
289-
JMP_BUF *bailout;
254+
zend_fiber_vm_state state;
290255

291-
ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout);
256+
ZEND_ASSERT(fiber->caller && "Fiber has no caller");
292257

293-
zend_fiber_suspend_context(&fiber->context);
294-
295-
ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout);
258+
zend_fiber_capture_vm_state(&state);
259+
zend_fiber_switch_context(zend_fiber_get_context(fiber->caller));
260+
zend_fiber_restore_vm_state(&state);
296261
}
297262

298263
static void zend_fiber_switch_to(zend_fiber *fiber)
299264
{
300-
zend_fiber *previous;
301-
zend_vm_stack stack;
302-
size_t stack_page_size;
303-
zend_execute_data *execute_data;
304-
int error_reporting;
305-
uint32_t jit_trace_num;
306-
JMP_BUF *bailout;
307-
308-
previous = EG(current_fiber);
265+
zend_fiber_context *context = zend_fiber_get_context(fiber);
266+
zend_fiber_vm_state state;
309267

310-
zend_observer_fiber_switch_notify(previous, fiber);
268+
zend_observer_fiber_switch_notify(EG(current_fiber), context);
311269

312-
ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout);
270+
fiber->caller = zend_fiber_from_context(EG(current_fiber));
313271

314-
EG(current_fiber) = fiber;
272+
zend_fiber_capture_vm_state(&state);
273+
zend_fiber_switch_context(context);
274+
zend_fiber_restore_vm_state(&state);
315275

316-
zend_fiber_switch_context(&fiber->context);
276+
fiber->caller = NULL;
317277

318-
EG(current_fiber) = previous;
319-
320-
ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout);
321-
322-
zend_observer_fiber_switch_notify(fiber, previous);
278+
zend_observer_fiber_switch_notify(context, EG(current_fiber));
323279

324280
if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_BAILOUT)) {
325281
// zend_bailout() was called in the fiber, so call it again in the previous fiber or {main}.
@@ -339,10 +295,9 @@ static zend_always_inline zend_vm_stack zend_fiber_vm_stack_alloc(size_t size)
339295
return page;
340296
}
341297

342-
static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context)
298+
static ZEND_STACK_ALIGNED zend_fiber_context *zend_fiber_execute(zend_fiber_context *context)
343299
{
344-
zend_fiber *fiber = EG(current_fiber);
345-
ZEND_ASSERT(fiber);
300+
zend_fiber *fiber = zend_fiber_from_context(context);
346301

347302
zend_long error_reporting = INI_INT("error_reporting");
348303
if (!error_reporting && !INI_STR("error_reporting")) {
@@ -396,6 +351,8 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context)
396351
zend_vm_stack_destroy();
397352
fiber->execute_data = NULL;
398353
fiber->stack_bottom = NULL;
354+
355+
return zend_fiber_get_context(fiber->caller);
399356
}
400357

401358
static zend_object *zend_fiber_object_create(zend_class_entry *ce)
@@ -454,7 +411,7 @@ static void zend_fiber_object_free(zend_object *object)
454411

455412
zval_ptr_dtor(&fiber->value);
456413

457-
zend_fiber_destroy_context(&fiber->context);
414+
zend_fiber_destroy_context(zend_fiber_get_context(fiber));
458415

459416
zend_object_std_dtor(&fiber->std);
460417
}
@@ -494,7 +451,7 @@ ZEND_API void zend_fiber_start(zend_fiber *fiber, zval *params, uint32_t param_c
494451
fiber->fci.param_count = param_count;
495452
fiber->fci.named_params = named_params;
496453

497-
if (!zend_fiber_init_context(&fiber->context, zend_fiber_execute, EG(fiber_stack_size))) {
454+
if (!zend_fiber_init_context(zend_fiber_get_context(fiber), zend_fiber_execute, EG(fiber_stack_size))) {
498455
RETURN_THROWS();
499456
}
500457

@@ -524,13 +481,13 @@ ZEND_METHOD(Fiber, start)
524481

525482
ZEND_API void zend_fiber_suspend(zval *value, zval *return_value)
526483
{
527-
zend_fiber *fiber = EG(current_fiber);
528-
529-
if (UNEXPECTED(!fiber)) {
484+
if (UNEXPECTED(EG(current_fiber) == EG(main_fiber))) {
530485
zend_throw_error(zend_ce_fiber_error, "Cannot suspend outside of a fiber");
531486
RETURN_THROWS();
532487
}
533488

489+
zend_fiber *fiber = zend_fiber_from_context(EG(current_fiber));
490+
534491
if (UNEXPECTED(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)) {
535492
zend_throw_error(zend_ce_fiber_error, "Cannot suspend in a force-closed fiber");
536493
RETURN_THROWS();
@@ -734,17 +691,13 @@ ZEND_METHOD(Fiber, getReturn)
734691

735692
ZEND_METHOD(Fiber, this)
736693
{
737-
zend_fiber *fiber;
738-
739694
ZEND_PARSE_PARAMETERS_NONE();
740695

741-
fiber = EG(current_fiber);
742-
743-
if (!fiber) {
696+
if (EG(current_fiber) == EG(main_fiber)) {
744697
RETURN_NULL();
745698
}
746699

747-
RETURN_OBJ_COPY(&fiber->std);
700+
RETURN_OBJ_COPY(&zend_fiber_from_context(EG(current_fiber))->std);
748701
}
749702

750703
ZEND_METHOD(FiberError, __construct)
@@ -775,5 +728,15 @@ void zend_register_fiber_ce(void)
775728

776729
void zend_fiber_init(void)
777730
{
778-
EG(current_fiber) = NULL;
731+
zend_fiber_context *context = ecalloc(1, sizeof(zend_fiber_context));
732+
733+
context->status = ZEND_FIBER_STATUS_RUNNING;
734+
735+
EG(main_fiber) = context;
736+
EG(current_fiber) = context;
737+
}
738+
739+
void zend_fiber_shutdown(void)
740+
{
741+
efree(EG(main_fiber));
779742
}

0 commit comments

Comments
 (0)