Skip to content

Commit 391ca94

Browse files
committed
ffi: first version using fibers for callbacks
1 parent 2f71fa3 commit 391ca94

File tree

3 files changed

+53
-66
lines changed

3 files changed

+53
-66
lines changed

Zend/zend_fibers.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ static void zend_fiber_cleanup(zend_fiber_context *context)
543543
fiber->caller = NULL;
544544
}
545545

546-
static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
546+
ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
547547
{
548548
ZEND_ASSERT(Z_TYPE(transfer->value) == IS_NULL && "Initial transfer value to fiber context must be NULL");
549549
ZEND_ASSERT(!transfer->flags && "No flags should be set on initial transfer");
@@ -615,7 +615,7 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
615615
}
616616

617617
/* Handles forwarding of result / error from a transfer into the running fiber. */
618-
static zend_always_inline void zend_fiber_delegate_transfer_result(
618+
zend_always_inline void zend_fiber_delegate_transfer_result(
619619
zend_fiber_transfer *transfer, INTERNAL_FUNCTION_PARAMETERS
620620
) {
621621
if (transfer->flags & ZEND_FIBER_TRANSFER_FLAG_ERROR) {
@@ -627,7 +627,7 @@ static zend_always_inline void zend_fiber_delegate_transfer_result(
627627
RETURN_COPY_VALUE(&transfer->value);
628628
}
629629

630-
static zend_always_inline zend_fiber_transfer zend_fiber_switch_to(
630+
zend_always_inline zend_fiber_transfer zend_fiber_switch_to(
631631
zend_fiber_context *context, zval *value, bool exception
632632
) {
633633
zend_fiber_transfer transfer = {
@@ -652,7 +652,7 @@ static zend_always_inline zend_fiber_transfer zend_fiber_switch_to(
652652
return transfer;
653653
}
654654

655-
static zend_always_inline zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception)
655+
zend_always_inline zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception)
656656
{
657657
zend_fiber *previous = EG(active_fiber);
658658

Zend/zend_fibers.h

+4
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,12 @@ struct _zend_fiber {
134134

135135
/* These functions may be used to create custom fiber objects using the bundled fiber switching context. */
136136
ZEND_API zend_result zend_fiber_init_context(zend_fiber_context *context, void *kind, zend_fiber_coroutine coroutine, size_t stack_size);
137+
ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer);
137138
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context);
138139
ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer);
140+
ZEND_API zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception);
141+
ZEND_API void zend_fiber_delegate_transfer_result(zend_fiber_transfer *transfer, INTERNAL_FUNCTION_PARAMETERS);
142+
ZEND_API zend_fiber_transfer zend_fiber_switch_to(zend_fiber_context *context, zval *value, bool exception);
139143
#ifdef ZEND_CHECK_STACK_LIMIT
140144
ZEND_API void* zend_fiber_stack_limit(zend_fiber_stack *stack);
141145
ZEND_API void* zend_fiber_stack_base(zend_fiber_stack *stack);

ext/ffi/ffi.c

+45-62
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
+----------------------------------------------------------------------+
1515
*/
1616

17+
#include "zend_API.h"
18+
#include "zend_globals.h"
1719
#ifdef HAVE_CONFIG_H
1820
# include "config.h"
1921
#endif
@@ -27,6 +29,8 @@
2729
#include "zend_weakrefs.h"
2830
#include "main/SAPI.h"
2931
#include "TSRM.h"
32+
#include "zend_fibers.h"
33+
#include "zend_stack.h"
3034

3135
#include <sys/types.h>
3236
#include <sys/stat.h>
@@ -907,7 +911,9 @@ static zend_always_inline zend_string *zend_ffi_mangled_func_name(zend_string *n
907911
/* }}} */
908912

909913
#if FFI_CLOSURES
914+
910915
typedef struct _zend_ffi_callback_data {
916+
zend_fiber fiber;
911917
zend_fcall_info_cache fcc;
912918
zend_ffi_type *type;
913919
void *code;
@@ -916,6 +922,7 @@ typedef struct _zend_ffi_callback_data {
916922
uint32_t arg_count;
917923
ffi_type *ret_type;
918924
ffi_type *arg_types[0] ZEND_ELEMENT_COUNT(arg_count);
925+
zend_ffi_call_data ffi_args;
919926
} zend_ffi_callback_data;
920927

921928
static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */
@@ -951,66 +958,18 @@ static void zend_ffi_dispatch_callback_end(void){ /* {{{ */
951958
}
952959
/* }}} */
953960

954-
static void zend_ffi_dispatch_callback(void){ /* {{{ */
955-
if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) {
956-
return;
957-
}
958-
959-
zend_ffi_callback_data *callback_data = FFI_G(callback_data).data;
960-
zend_fcall_info fci;
961-
zend_ffi_type *ret_type;
962-
zval retval;
963-
ALLOCA_FLAG(use_heap)
964-
965-
fci.size = sizeof(zend_fcall_info);
966-
ZVAL_UNDEF(&fci.function_name);
967-
fci.retval = &retval;
968-
fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap);
969-
fci.object = NULL;
970-
fci.param_count = callback_data->arg_count;
971-
fci.named_params = NULL;
972-
973-
974-
if (callback_data->type->func.args) {
975-
int n = 0;
976-
zend_ffi_type *arg_type;
977-
978-
ZEND_HASH_PACKED_FOREACH_PTR(callback_data->type->func.args, arg_type) {
979-
arg_type = ZEND_FFI_TYPE(arg_type);
980-
zend_ffi_cdata_to_zval(NULL, FFI_G(callback_data).args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
981-
n++;
982-
} ZEND_HASH_FOREACH_END();
983-
}
984-
985-
ZVAL_UNDEF(&retval);
986-
if (zend_call_function(&fci, &callback_data->fcc) != SUCCESS) {
987-
zend_throw_error(zend_ffi_exception_ce, "Cannot call callback");
988-
}
989-
990-
if (callback_data->arg_count) {
991-
int n = 0;
992-
993-
for (n = 0; n < callback_data->arg_count; n++) {
994-
zval_ptr_dtor(&fci.params[n]);
995-
}
996-
}
997-
free_alloca(fci.params, use_heap);
998-
999-
if (EG(exception)) {
1000-
// we're about to do a hard exit. unlock all mutexes
1001-
zend_ffi_dispatch_callback_end();
1002-
tsrm_mutex_unlock(FFI_G(vm_request_lock));
1003-
zend_error_noreturn(E_ERROR, "Throwing from FFI callbacks is not allowed");
1004-
}
1005-
1006-
ret_type = ZEND_FFI_TYPE(callback_data->type->func.ret_type);
1007-
if (ret_type->kind != ZEND_FFI_TYPE_VOID) {
1008-
zend_ffi_zval_to_cdata(FFI_G(callback_data).ret, ret_type, &retval);
1009-
}
1010-
1011-
zval_ptr_dtor(&retval);
961+
static void zend_ffi_fci_prepare(
962+
zend_ffi_callback_data *data,
963+
zend_fcall_info *fci
964+
){
965+
fci->size = sizeof(*fci);
966+
ZVAL_UNDEF(&fci->function_name);
967+
fci->retval = &data->fiber.result;
968+
fci->params = emalloc(sizeof(zval) * data->arg_count);
969+
fci->object = NULL;
970+
fci->param_count = data->arg_count;
971+
fci->named_params = NULL;
1012972
}
1013-
/* }}} */
1014973

1015974
static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */
1016975
tsrm_mutex_lock(FFI_G(vm_request_lock));
@@ -1079,6 +1038,8 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
10791038
.args = args,
10801039
.data = (zend_ffi_callback_data *)data
10811040
};
1041+
((zend_ffi_callback_data *)data)->ffi_args = call_data;
1042+
10821043
FFI_G(callback_data) = call_data;
10831044

10841045
FFI_G(callback_tid) = tsrm_thread_id();
@@ -1096,8 +1057,25 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
10961057
}
10971058

10981059
// dispatch the callback
1099-
zend_ffi_dispatch_callback();
1100-
1060+
if (call_data.data->type->func.args) {
1061+
int n = 0;
1062+
zend_ffi_type *arg_type;
1063+
1064+
ZEND_HASH_PACKED_FOREACH_PTR(call_data.data->type->func.args, arg_type) {
1065+
arg_type = ZEND_FFI_TYPE(arg_type);
1066+
zend_ffi_cdata_to_zval(NULL,
1067+
call_data.data->ffi_args.args[n], arg_type, BP_VAR_R,
1068+
&call_data.data->fiber.fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST),
1069+
0, 0);
1070+
n++;
1071+
} ZEND_HASH_FOREACH_END();
1072+
}
1073+
1074+
if (zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) {
1075+
zend_fiber_resume(&call_data.data->fiber, NULL, false);
1076+
efree(call_data.data->fiber.fci.params);
1077+
}
1078+
11011079
zend_ffi_dispatch_callback_end();
11021080
}
11031081
tsrm_mutex_unlock(FFI_G(vm_request_lock));
@@ -1141,6 +1119,12 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ *
11411119
callback_data->callback = callback;
11421120
callback_data->code = code;
11431121
callback_data->arg_count = arg_count;
1122+
callback_data->fiber.fci_cache = fcc;
1123+
1124+
zend_fiber_init_context(&callback_data->fiber.context, NULL, zend_fiber_execute, EG(fiber_stack_size));
1125+
callback_data->fiber.previous = &callback_data->fiber.context;
1126+
1127+
zend_ffi_fci_prepare(callback_data, &callback_data->fiber.fci);
11441128

11451129
if (type->func.args) {
11461130
int n = 0;
@@ -5763,7 +5747,6 @@ static ZEND_GINIT_FUNCTION(ffi)
57635747
/* {{{ ZEND_GINIT_FUNCTION */
57645748
static ZEND_GSHUTDOWN_FUNCTION(ffi)
57655749
{
5766-
tsrm_mutex_free(ffi_globals->vm_request_lock);
57675750
zend_ffi_wait_request_barrier(true);
57685751
if (ffi_globals->scopes) {
57695752
zend_hash_destroy(ffi_globals->scopes);

0 commit comments

Comments
 (0)