From 4563e0fb157d458ee5b70a67bc444b7b94dbf456 Mon Sep 17 00:00:00 2001 From: codinghuang <2812240764@qq.com> Date: Mon, 1 Jul 2019 00:29:53 +0800 Subject: [PATCH] update the code --- README.md | 1 + config.m4 | 1 + ...72\357\274\210\344\270\203\357\274\211.md" | 42 +++++++++++ ...72\357\274\210\345\205\255\357\274\211.md" | 5 +- include/asm_context.h | 11 +++ include/context.h | 25 +++++++ include/coroutine.h | 26 +++++++ src/coroutine/context.cc | 40 +++++++++++ src/coroutine/coroutine.cc | 17 +++++ study_coroutine.cc | 69 ++++++++++++++++++- study_coroutine.h | 6 ++ 11 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 "docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\344\270\203\357\274\211.md" create mode 100644 include/asm_context.h create mode 100644 src/coroutine/context.cc diff --git a/README.md b/README.md index 032e275..d90ac00 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,4 @@ PHP版本:7.3.5 [11、协程创建(六)](./docs/《PHP扩展开发》-协程-协程创建(六).md) +[12、协程创建(七)](./docs/《PHP扩展开发》-协程-协程创建(七).md) \ No newline at end of file diff --git a/config.m4 b/config.m4 index 2b2f59d..11d8636 100644 --- a/config.m4 +++ b/config.m4 @@ -51,6 +51,7 @@ if test "$PHP_STUDY" != "no"; then study_coroutine.cc \ study_coroutine_util.cc \ src/coroutine/coroutine.cc \ + src/coroutine/context.cc \ ${STUDY_ASM_DIR}make_${STUDY_CONTEXT_ASM_FILE} \ ${STUDY_ASM_DIR}jump_${STUDY_CONTEXT_ASM_FILE} " diff --git "a/docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\344\270\203\357\274\211.md" "b/docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\344\270\203\357\274\211.md" new file mode 100644 index 0000000..f82c3ec --- /dev/null +++ "b/docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\344\270\203\357\274\211.md" @@ -0,0 +1,42 @@ +# 协程创建(七) + +我们在上篇文章,成功的保存了主协程的上下文信息,现在,我们就需要为我们的任务函数创建协程了。 + +我们在`PHPCoroutine::create`中写入: + +```cpp +long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv) +{ + php_coro_args php_coro_args; + php_coro_args.fci_cache = fci_cache; + php_coro_args.argv = argv; + php_coro_args.argc = argc; + save_task(get_task()); + + return Coroutine::create(create_func, (void*) &php_coro_args); +} +``` + +其中,`PHPCoroutine::create_func`是用来创建协程任务函数的。它是: + +```cpp +typedef void(* coroutine_func_t)(void *) +``` + +类型的函数指针。 + +而`php_coro_args`则是传递给`create_func`的参数。 + +OK,我们现在来实现一下`PHPCoroutine::create_func`。我们先在`Study::PHPCoroutine`类中声明一下这个方法: + +```cpp +protected: + static void create_func(void *arg); +``` + +然后,我们在文件`study_coroutine.cc`中来实现这个函数: + +```cpp + +``` + diff --git "a/docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\345\205\255\357\274\211.md" "b/docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\345\205\255\357\274\211.md" index 0522319..96ee71b 100644 --- "a/docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\345\205\255\357\274\211.md" +++ "b/docs/\343\200\212PHP\346\211\251\345\261\225\345\274\200\345\217\221\343\200\213-\345\215\217\347\250\213-\345\215\217\347\250\213\345\210\233\345\273\272\357\274\210\345\205\255\357\274\211.md" @@ -104,4 +104,7 @@ protected: void *task = nullptr; ``` -这样,我们就实现了保存`PHP`协程栈的功能。 \ No newline at end of file +这样,我们就实现了保存`PHP`协程栈的功能。 + +[下一篇:协程创建(七)](./《PHP扩展开发》-协程-协程创建(七).md) + diff --git a/include/asm_context.h b/include/asm_context.h new file mode 100644 index 0000000..2763579 --- /dev/null +++ b/include/asm_context.h @@ -0,0 +1,11 @@ +#ifndef ASM_CONTEXT_H +#define ASM_CONTEXT_H + +#include "study.h" + +typedef void* fcontext_t; + +intptr_t jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); +fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); + +#endif /* ASM_CONTEXT_H */ \ No newline at end of file diff --git a/include/context.h b/include/context.h index 2d87c7f..9c01d09 100644 --- a/include/context.h +++ b/include/context.h @@ -1,6 +1,31 @@ #ifndef CONTEXT_H #define CONTEXT_H +#include "asm_context.h" + +typedef fcontext_t coroutine_context_t; typedef void (*coroutine_func_t)(void*); +namespace Study +{ +class Context +{ +public: + Context(size_t stack_size, coroutine_func_t fn, void* private_data); + ~Context(); + bool swap_in(); + bool swap_out(); + static void context_func(void* arg); // coroutine entry function + +protected: + coroutine_context_t ctx_; + coroutine_context_t swap_ctx_; + coroutine_func_t fn_; + char* stack_; + uint32_t stack_size_; + void *private_data_; + bool end_; +}; +} + #endif /* CONTEXT_H */ diff --git a/include/coroutine.h b/include/coroutine.h index 2f0e869..3d0e5c8 100644 --- a/include/coroutine.h +++ b/include/coroutine.h @@ -2,19 +2,45 @@ #define COROUTINE_H #include "context.h" +#include namespace Study { class Coroutine { public: + static std::unordered_map coroutines; + static void* get_current_task(); static long create(coroutine_func_t fn, void* args = nullptr); void* get_task(); + static Coroutine* get_current(); + void set_task(void *_task); protected: + Coroutine *origin; static Coroutine* current; void *task = nullptr; + static size_t stack_size; + Context ctx; + long cid; + static long last_cid; + + Coroutine(coroutine_func_t fn, void *private_data) : + ctx(stack_size, fn, private_data) + { + cid = ++last_cid; + coroutines[cid] = this; + } + + inline long run() + { + long cid = this->cid; + origin = current; + current = this; + ctx.swap_in(); + return cid; + } }; } diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc new file mode 100644 index 0000000..b32ac5f --- /dev/null +++ b/src/coroutine/context.cc @@ -0,0 +1,40 @@ +#include "context.h" +#include "study.h" + + +using Study::Context; + +Context::Context(size_t stack_size, coroutine_func_t fn, void* private_data) : + fn_(fn), stack_size_(stack_size), private_data_(private_data) +{ +#ifdef SW_CONTEXT_PROTECT_STACK_PAGE + protect_page_ = 0; +#endif + end_ = false; + swap_ctx_ = nullptr; + + stack_ = (char*) malloc(stack_size_); + + void* sp = (void*) ((char*) stack_ + stack_size_); + ctx_ = make_fcontext(sp, stack_size_, (void (*)(intptr_t))&context_func); +} + +bool Context::swap_in() +{ + jump_fcontext(&swap_ctx_, ctx_, (intptr_t) this, true); + return true; +} + +bool Context::swap_out() +{ + jump_fcontext(&ctx_, swap_ctx_, (intptr_t) this, true); + return true; +} + +void Context::context_func(void *arg) +{ + Context *_this = (Context *) arg; + _this->fn_(_this->private_data_); + _this->end_ = true; + _this->swap_out(); +} \ No newline at end of file diff --git a/src/coroutine/coroutine.cc b/src/coroutine/coroutine.cc index c7f3c06..fe23c9e 100644 --- a/src/coroutine/coroutine.cc +++ b/src/coroutine/coroutine.cc @@ -3,6 +3,8 @@ using Study::Coroutine; Coroutine* Coroutine::current = nullptr; +long Coroutine::last_cid = 0; +std::unordered_map Coroutine::coroutines; void* Coroutine::get_current_task() { @@ -12,4 +14,19 @@ void* Coroutine::get_current_task() void* Coroutine::get_task() { return task; +} + +Coroutine* Coroutine::get_current() +{ + return current; +} + +void Coroutine::set_task(void *_task) +{ + task = _task; +} + +long Coroutine::create(coroutine_func_t fn, void* args) +{ + return (new Coroutine(fn, args))->run(); } \ No newline at end of file diff --git a/study_coroutine.cc b/study_coroutine.cc index affa3ac..ef82c0b 100644 --- a/study_coroutine.cc +++ b/study_coroutine.cc @@ -12,7 +12,8 @@ long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval php_coro_args.argv = argv; php_coro_args.argc = argc; save_task(get_task()); - return 0; + + return Coroutine::create(create_func, (void*) &php_coro_args); } php_coro_task* PHPCoroutine::get_task() @@ -34,3 +35,69 @@ void PHPCoroutine::save_vm_stack(php_coro_task *task) task->vm_stack_page_size = EG(vm_stack_page_size); task->execute_data = EG(current_execute_data); } + +void PHPCoroutine::create_func(void *arg) +{ + int i; + php_coro_args *php_arg = (php_coro_args *) arg; + zend_fcall_info_cache fci_cache = *php_arg->fci_cache; + zend_function *func = fci_cache.function_handler; + zval *argv = php_arg->argv; + int argc = php_arg->argc; + php_coro_task *task; + zend_execute_data *call; + zval _retval, *retval = &_retval; + + vm_stack_init(); // get a new php stack + call = (zend_execute_data *) (EG(vm_stack_top)); + task = (php_coro_task *) EG(vm_stack_top); + EG(vm_stack_top) = (zval *) ((char *) call + PHP_CORO_TASK_SLOT * sizeof(zval)); + + call = zend_vm_stack_push_call_frame( + ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED, + func, argc, fci_cache.called_scope, fci_cache.object + ); + + for (i = 0; i < argc; ++i) + { + zval *param; + zval *arg = &argv[i]; + param = ZEND_CALL_ARG(call, i + 1); + ZVAL_COPY(param, arg); + } + + call->symbol_table = NULL; + + EG(current_execute_data) = call; + + save_vm_stack(task); + + task->co = Coroutine::get_current(); + task->co->set_task((void *) task); + + if (func->type == ZEND_USER_FUNCTION) + { + ZVAL_UNDEF(retval); + EG(current_execute_data) = NULL; + zend_init_func_execute_data(call, &func->op_array, retval); + zend_execute_ex(EG(current_execute_data)); + } + + zval_ptr_dtor(retval); +} + +void PHPCoroutine::vm_stack_init(void) +{ + uint32_t size = DEFAULT_PHP_STACK_PAGE_SIZE; + zend_vm_stack page = (zend_vm_stack) emalloc(size); + + page->top = ZEND_VM_STACK_ELEMENTS(page); + page->end = (zval*) ((char*) page + size); + page->prev = NULL; + + EG(vm_stack) = page; + EG(vm_stack)->top++; + EG(vm_stack_top) = EG(vm_stack)->top; + EG(vm_stack_end) = EG(vm_stack)->end; + EG(vm_stack_page_size) = size; +} diff --git a/study_coroutine.h b/study_coroutine.h index ec68d14..9edc250 100644 --- a/study_coroutine.h +++ b/study_coroutine.h @@ -4,6 +4,9 @@ #include "php_study.h" #include "coroutine.h" +#define DEFAULT_PHP_STACK_PAGE_SIZE 8192 +#define PHP_CORO_TASK_SLOT ((int)((ZEND_MM_ALIGNED_SIZE(sizeof(php_coro_task)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval)) - 1) / ZEND_MM_ALIGNED_SIZE(sizeof(zval)))) + struct php_coro_args { zend_fcall_info_cache *fci_cache; @@ -19,6 +22,7 @@ struct php_coro_task zend_vm_stack vm_stack; // current coroutine stack pointer size_t vm_stack_page_size; zend_execute_data *execute_data; // current coroutine stack frame + Study::Coroutine *co; }; namespace Study @@ -34,6 +38,8 @@ protected: static void save_task(php_coro_task *task); static void save_vm_stack(php_coro_task *task); static php_coro_task* get_task(); + static void create_func(void *arg); + static void vm_stack_init(void); }; } -- GitLab