├── .gitattributes ├── .gitignore ├── .travis.yml ├── CREDITS ├── EXPERIMENTAL ├── LICENSE ├── README.md ├── REFERENCE_CN.md ├── REFERENCE_EN.md ├── config.m4 ├── config.w32 ├── docker ├── CentOS7-Base-163.repo ├── Dockerfile ├── build.bash └── php-fpm.conf ├── examples ├── chan_greetings.php ├── chan_read_write_closed_chan.php ├── chan_trypop_simulate_pop.php ├── chan_trypush_simulate_push.php ├── select_block.phpt ├── select_producer_consumer.php ├── select_read_default.php ├── selector_loop.php └── selector_select.php ├── md ├── cn │ ├── chan-close.md │ ├── chan-construct.md │ ├── chan-destruct.md │ ├── chan-pop.md │ ├── chan-push.md │ ├── chan-trypop.md │ ├── chan-trypush.md │ ├── chan.md │ ├── go.md │ ├── goo.md │ ├── mutex-construct.md │ ├── mutex-destruct.md │ ├── mutex-islock.md │ ├── mutex-lock.md │ ├── mutex-trylock.md │ ├── mutex-unlock.md │ ├── mutex.md │ ├── runtime-goid.md │ ├── runtime-gosched.md │ ├── runtime-numGoroutine.md │ ├── runtime.md │ ├── scheduler-join.md │ ├── scheduler-loop.md │ ├── scheduler-run.md │ ├── scheduler.md │ ├── select.md │ ├── selector-construct.md │ ├── selector-loop.md │ ├── selector-select.md │ ├── selector.md │ ├── time-after.md │ ├── time-sleep.md │ ├── time-tick.md │ ├── time.md │ ├── waitgroup-add.md │ ├── waitgroup-construct.md │ ├── waitgroup-destruct.md │ ├── waitgroup-done.md │ ├── waitgroup-wait.md │ └── waitgroup.md └── en │ ├── chan-close.md │ ├── chan-construct.md │ ├── chan-destruct.md │ ├── chan-pop.md │ ├── chan-push.md │ ├── chan-trypop.md │ ├── chan-trypush.md │ ├── chan.md │ ├── go.md │ ├── mutex-construct.md │ ├── mutex-destruct.md │ ├── mutex-islock.md │ ├── mutex-lock.md │ ├── mutex-trylock.md │ ├── mutex-unlock.md │ ├── mutex.md │ ├── runtime-goid.md │ ├── runtime-gosched.md │ ├── runtime-numGoroutine.md │ ├── runtime.md │ ├── scheduler-join.md │ ├── scheduler-loop.md │ ├── scheduler-run.md │ ├── scheduler.md │ ├── select.md │ ├── selector-construct.md │ ├── selector-loop.md │ ├── selector-select.md │ ├── selector.md │ ├── time-after.md │ ├── time-sleep.md │ ├── time-tick.md │ ├── time.md │ ├── waitgroup-add.md │ ├── waitgroup-construct.md │ ├── waitgroup-destruct.md │ ├── waitgroup-done.md │ ├── waitgroup-wait.md │ └── waitgroup.md ├── src ├── basic_functions.cc ├── co_recursive_mutex.h ├── co_wait_group.h ├── defer.h ├── freeable.h ├── global_defs.h ├── go.cc ├── go.h ├── go_chan.cc ├── go_chan.h ├── go_mutex.h ├── go_runtime.cc ├── go_runtime.h ├── go_scheduler.cc ├── go_scheduler.h ├── go_select.cc ├── go_select.h ├── go_time.cc ├── go_time.h ├── go_wait_group.h ├── php_phpgo.h ├── php_version_dependent.h ├── phpgo.cc ├── phpgo_context.cc ├── phpgo_context.h ├── stdinc.h ├── task_listener.h ├── task_local_storage.cc ├── task_local_storage.h ├── test_mutex.cc ├── zend_variables_persist.cc └── zend_variables_persist.h ├── test_php └── tests ├── 001_phpgo_presence.phpt ├── 002_go_hello_world.phpt ├── 003_go_named_function.phpt ├── 004_go_closure_with_args.phpt ├── 005_go_closure_with_128_args.phpt ├── 006_go_closure_with_256_args.phpt ├── 007_go_closure_with_256_local_vars.phpt ├── 008_go_closure_with_1024_local_vars.phpt ├── 009_go_recursive_function_128_levels.phpt ├── 010_go_recursive_function_4096_levels.phpt ├── 011_go_routines_of_16_nested_levels.phpt ├── 012_go_routines_of_10240_concurrence.phpt__ ├── 021_go_chan_create.phpt ├── 022_go_chan_create_error_1.phpt ├── 023_go_chan_create_error_2.phpt ├── 024_go_chan_create_error_3.phpt ├── 025_go_chan_create_error_4.phpt ├── 026_go_chan_create_error_5.phpt ├── 027_go_chan_create_error_6.phpt ├── 029_go_chan_pop_outside_of_go_routine.phpt ├── 030_go_chan_1_push_pop.phpt ├── 031_go_chan_0_push_pop.phpt ├── 032_go_chan_push_pop_array.phpt ├── 033_go_chan_push_pop_object.phpt ├── 034_go_chan_1_copy_push_pop.phpt ├── 035_go_chan_0_copy_push_pop.phpt ├── 036_go_chan_10_copy_push_pop.phpt ├── 037_go_chan_10_push_pop.phpt ├── 038_go_chan_trypush_trypop_close.phpt ├── 039_go_chan_close_warnings.phpt ├── 040_go_select_read_select_write.phpt ├── 041_go_select_read_write_default.phpt ├── 042_go_select_read_write.phpt ├── 043_go_select_read_write_default_timer.phpt ├── 044_go_select_short_cut_syntax.phpt ├── 045_go_select_error_invalid_case_type.phpt ├── 046_go_select_error_invalid_channel_parameter.phpt ├── 047_go_select_error_insufficient_parameters.phpt ├── 048_go_select_error_insufficient_parameters_2.phpt ├── 050_go_redis_con_shared_by_muti_go_routines.phpt ├── 051_go_redis_dedicate_cons_by_muti_go_routines.phpt ├── 060_go_mysql_con_shared_by_muti_go_routines.phpt ├── 061_go_mysql_dedicate_cons_by_muti_go_routines.phpt ├── 071_go_waitgroup_test.phpt ├── 081_go_mutex_basic_test.phpt ├── 082_go_mutex_recursively_lock.phpt ├── 091_go_runtime_basic_test.phpt ├── 101_go_routines_with_swoole_server.phpt__ ├── 102_go_routines_with_swoole_server_nogo.phpt__ ├── 111_go_scheduler_run.phpt ├── 112_go_scheduler_goid.phpt ├── 121_go_super_globals_inherits.phpt ├── 131_go_time_basic_test.phpt ├── 141_go_exit.phpt ├── 151_go_compatibility_file_get_content.phpt ├── 153_go_compatibility_curl.phpt ├── 161_go_shutdown_func.phpt ├── 162_go_error_handler.phpt └── known_issues └── block_object_was_waiting_when_destructor.php /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-language=c++ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.lo 3 | *.la 4 | .libs 5 | acinclude.m4 6 | aclocal.m4 7 | autom4te.cache 8 | build 9 | config.guess 10 | config.h 11 | config.h.in 12 | config.log 13 | config.nice 14 | config.status 15 | config.sub 16 | configure 17 | configure.in 18 | configure.ac 19 | tmp-php.ini 20 | include 21 | install-sh 22 | libtool 23 | ltmain.sh 24 | Makefile 25 | Makefile.fragments 26 | Makefile.global 27 | Makefile.objects 28 | missing 29 | mkinstalldirs 30 | modules 31 | run-tests.php 32 | tests/*.diff 33 | tests/*.out 34 | tests/*.php 35 | tests/*.exp 36 | tests/*.log 37 | tests/*.sh 38 | core.* 39 | ftp-sync.json 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: cpp 4 | language: php 5 | 6 | compiler: 7 | - gcc 8 | 9 | php: 10 | - '7.0.0' 11 | - '7.0' 12 | - '7.1.0' 13 | - '7.1' 14 | - '7.2.0' 15 | - '7.2' 16 | - '5.4' 17 | - '5.5' 18 | - '5.6' 19 | 20 | before_install: 21 | - php --ini 22 | - sudo add-apt-repository -y ppa:kojoley/boost 23 | - sudo apt-get update -y || echo "update error" 24 | - lsb_release -a 25 | 26 | install: 27 | - git clone https://github.com/yyzybb537/Boost-dev-bin.git /tmp/boost-dev-bin && sudo dpkg -i /tmp/boost-dev-bin/libboost1.59-all-dev.deb 28 | - ls /usr/local/lib/libboost* 29 | - sudo ldconfig 30 | - sudo apt-get install -y -qq cmake 31 | - git clone https://github.com/birdwyx/libgo /tmp/libgo 32 | - pushd . 33 | - cd /tmp/libgo && git checkout hook_getaddrinfo && git pull && mkdir build && cd build 34 | - sudo rm * -rf && cmake .. -DENABLE_BOOST_CONTEXT=ON && make -j4 && sudo make install 35 | - sudo ldconfig 36 | 37 | script: 38 | - popd 39 | - phpize 40 | - ./configure --with-php-config=`which php-config` 41 | - make clean && make && make install 42 | - echo "extension=phpgo.so" > `php --ini | grep "Scan for additional" | awk '{print $7 "/phpgo.ini"}'` 43 | - echo "export LD_PRELOAD=liblibgo.so" > test_php && echo `which php` \"\$\@\" >> test_php && chmod 755 test_php 44 | - export TEST_PHP_EXECUTABLE=`pwd`/test_php 45 | - php run-tests.php tests | tee test_result.txt 46 | - cat test_result.txt | grep -A 10000 'FAILED TEST SUMMARY' | grep tests | sed 's/.*\[//g' | sed 's/\.phpt\]/\.log/g' | xargs cat 47 | - echo | exit `cat test_result.txt | grep 'Tests failed' | awk '{print $4}'` 48 | 49 | after_success: 50 | - echo "build succeeded" 51 | 52 | branches: 53 | only: 54 | - dev 55 | - master 56 | 57 | notifications: 58 | email: true 59 | 60 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | phpgo -------------------------------------------------------------------------------- /EXPERIMENTAL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/EXPERIMENTAL -------------------------------------------------------------------------------- /REFERENCE_CN.md: -------------------------------------------------------------------------------- 1 | # Phpgo 参考手册 2 | 3 | ## 简介 4 | ## 安装/配置 5 | ### 需求 6 | - phpgo目前支持linux操作系统 7 | - phpgo依赖c++协程库libgo实现底层协程特性。在安装phpgo之前,必须先安装好libgo 8 | - libgo依赖boost context以获得最优的协程切换性能,因此在安装libgo之前建议安装boost 9 | ### 安装 10 | #### 1. 安装boost(可选) 11 | ``` 12 | wget https://sourceforge.net/projects/boost/files/boost/1.59.0/boost_1_59_0.tar.gz 13 | tar -xvf boost_1_59_0.tar.gz 14 | cd boost_1_59_0 15 | ./bootstrap.sh 16 | ./b2 -q install 17 | ``` 18 | 19 | #### 2. 安装libgo 20 | 通过以下步骤安装libgo: 21 | ``` 22 | git clone https://github.com/yyzybb537/libgo 23 | cd libgo 24 | git checkout master 25 | mkdir build 26 | cd build 27 | rm -rf * 28 | cmake .. -DENABLE_BOOST_CONTEXT=ON #如果未安装boost,则替换为 cmake .. 29 | make 30 | make install 31 | ldconfig 32 | ``` 33 | 34 | #### 3. 安装phpgo 35 | 36 | 通过以下步骤安装phpgo: 37 | ``` 38 | git clone https://github.com/birdwyx/phpgo 39 | cd phpgo 40 | phpize 41 | ./configure -with-php-config= 42 | make 43 | make install 44 | ``` 45 | 46 | ### 运行时配置 47 | #### 1. 在php.ini 中添加如下配置 48 | ``` 49 | extension=phpgo.so 50 | ``` 51 | #### 2. 命令行方式 52 | ``` 53 | export LD_PRELOAD=liblibgo.so 54 | php my_app.php 55 | unset -v LD_PRELOAD 56 | ``` 57 | 注意在运行之前设置了环境变量LD_PRELOAD。这样做得原因是你可以通过LD_PRELOAD 让你的php代码及第三方扩展中的涉及I/O操作的同步函数调用(如redis、mysql数据读写,网络读写,以及sleep/usleep等)自动“异步化”,在协程中调用这些涉及IO操作的函数时,当前协程会自动切出,将执行权利让给其他协程。 58 | 59 | 当然你也可以选择不设置LD_PRELOAD,这样你的代码及第三方扩展中的同步I/O操作会维持同步,在这些操作完成前,其他协程,甚至是调度器,在该操作完成之前不会运行。 60 | 61 | 如果你是在命令行下玩phpgo,运行完你的App之后,清除一下LD_PRELOAD,因为提前加载的libgo虽然和phpgo可以很好的配合,但未必不会跟linux下其他应用程序打架。 62 | 63 | 最好是做一个脚本,将LD_PRELOAD和你的php程序包装进去: 64 | ``` 65 | my_app.sh: 66 | export LD_PRELOAD=liblibgo.so; 67 | php my_app.php; 68 | ``` 69 | 然后运行 my_app.sh 来启动你的应用程序 70 | ``` 71 | ./my_app.sh& 72 | ``` 73 | 74 | #### 3. php-fpm方式 75 | 在php-fpm服务管理脚本(通常是 /etc/init.d/php-fpm)中 “start)” 一节中加入以下一行 76 | ``` 77 | export LD_PRELOAD=liblibgo.so 78 | ``` 79 | 加完后,php-fpm脚本大概会长这样: 80 | ``` 81 | /etc/init.d/php-fpm: 82 | ... 83 | case "$1" in 84 | start) 85 | echo -n "Starting php-fpm - with libgo preloaded" 86 | export LD_PRELOAD=liblibgo.so 87 | $php_fpm_BIN --daemonize $php_opts 88 | if [ "$?" != 0 ] ; then 89 | echo " failed" 90 | exit 1 91 | fi 92 | ... 93 | ``` 94 | 然后,命令行执行 service php-fpm restart重启php-fpm使改动生效 95 | ``` 96 | service php-fpm restart 97 | ``` 98 | 99 | ## phpgo函数 100 | - [go](https://github.com/birdwyx/phpgo/blob/master/md/cn/go.md) — 创建go routine 101 | - [goo](https://github.com/birdwyx/phpgo/blob/master/md/cn/goo.md) — 创建go routine,支持可选参数 102 | - [select](https://github.com/birdwyx/phpgo/blob/master/md/cn/select.md) — 进行一次事件轮询,并返回一个Selector对象 103 | 104 | ## [go\Scheduler](https://github.com/birdwyx/phpgo/blob/master/md/cn/scheduler.md) — go协程调度器类:Scheduler 105 | - [go\Scheduler::join](https://github.com/birdwyx/phpgo/blob/master/md/cn/scheduler-join.md) — 运行协程调度器直到所有协程运行完成 106 | - [go\Scheduler::loop](https://github.com/birdwyx/phpgo/blob/master/md/cn/scheduler-loop.md) — 运行协程调度器直到调度线程退出 107 | - [go\Scheduler::run](https://github.com/birdwyx/phpgo/blob/master/md/cn/scheduler-run.md) — 运行一次协程调度器,检查一遍所有待调度协程,如果协程就绪就运行该协程一次 108 | 109 | ## [go\Selector](https://github.com/birdwyx/phpgo/blob/master/md/cn/scheduler.md) — Selector类 110 | - [go\Selector::__construct](https://github.com/birdwyx/phpgo/blob/master/md/cn/selector-construct.md) — 创建Selector. 提供给select()调用,php代码不应直接调用Selector构造器 111 | - [go\Selector::select](https://github.com/birdwyx/phpgo/blob/master/md/cn/selector-select.md) — 进行一次事件轮询,返回本Selector对象 112 | - [go\Selector::loop](https://github.com/birdwyx/phpgo/blob/master/md/cn/selector-loop.md) — 运行事件轮询直到指定的Channel有数据加入或Channel关闭 113 | 114 | ## [go\Chan](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan.md) — 管道类:Chan 115 | - [go\Chan::close](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-close.md) — 关闭管道 116 | - [go\Chan::__construct](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-construct.md) — 创建管道 117 | - [go\Chan::__destruct](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-destruct.md) — 销毁管道 118 | - [go\Chan::pop](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-pop.md) — 从管道头部读取一条数据,如果没有数据则阻塞,如果管道关闭则返回NULL,如果出错则返回FALSE 119 | - [go\Chan::push](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-push.md) — 在管道尾部添加一条数据,如果成功则返回TRUE,如果管道已满、管道关闭或出错则返回FALSE 120 | - [go\Chan::tryPop](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-trypop.md) — 从管道头部读取一条数据,如果管道关闭则返回NULL,如果没有数据或出错则返回FALSE, 121 | - [go\Chan::tryPush](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-trypush.md) — 在管道尾部添加一条数据,如果成功则返回TRUE,如果管道已满、管道关闭或出错则返回FALSE 122 | 123 | ## [go\WaitGroup](https://github.com/birdwyx/phpgo/blob/master/md/cn/waitgroup.md) — WaitGroup类 124 | - [go\WaitGroup::Add](https://github.com/birdwyx/phpgo/blob/master/md/cn/waitgroup-add.md) — 增加WaitGroup等待计数,可指定增加数量 125 | - [go\WaitGroup::__construct](https://github.com/birdwyx/phpgo/blob/master/md/cn/waitgroup-construct.md) — 创建WaitGroup 126 | - [go\WaitGroup::__destruct](https://github.com/birdwyx/phpgo/blob/master/md/cn/waitgroup-destruct.md) — 销毁WaitGroup 127 | - [go\WaitGroup::Done](https://github.com/birdwyx/phpgo/blob/master/md/cn/waitgroup-done.md) — WaitGroup等待计数减一,相当于WaitGroup::Add(-1) 128 | - [go\WaitGroup::Wait](https://github.com/birdwyx/phpgo/blob/master/md/cn/waitgroup-wait.md) — 等待WaitGroup的等待计数减小到0,调用者为协程时协程挂起,调用者为调度线程时,则运行调度器直到等待计数减为0 129 | 130 | ## [go\Mutex](https://github.com/birdwyx/phpgo/blob/master/md/cn/mutex.md) — 互斥锁类:Mutex 131 | - [go\Mutex::__construct](https://github.com/birdwyx/phpgo/blob/master/md/cn/mutex-construct.md) — 创建协程互斥锁 132 | - [go\Mutex::__destruct](https://github.com/birdwyx/phpgo/blob/master/md/cn/mutex-destruct.md) — 销毁协程互斥锁 133 | - [go\Mutex::isLock](https://github.com/birdwyx/phpgo/blob/master/md/cn/mutex-islock.md) — 检查互斥锁是已被加锁 134 | - [go\Mutex::lock](https://github.com/birdwyx/phpgo/blob/master/md/cn/mutex-lock.md) — 获取互斥锁,如果互斥锁已被其让协程获取,则阻塞;获得互斥锁的协程可以重复获取而不会阻塞 135 | - [go\Mutex::tryLock](https://github.com/birdwyx/phpgo/blob/master/md/cn/mutex-trylock.md) — 获取互斥锁,获取成功返回TRUE,如果互斥锁已被其让协程获取,返回FALSE 136 | - [go\Mutex::unlock](https://github.com/birdwyx/phpgo/blob/master/md/cn/mutex-unlock.md) — 释放互斥锁 137 | 138 | ## [go\Time](https://github.com/birdwyx/phpgo/blob/master/md/cn/time.md) — Time类 139 | - [go\Time::after](https://github.com/birdwyx/phpgo/blob/master/md/cn/time-after.md) — 创建并返回一个Channel,在指定纳秒时间后phpgo将当时的精确时间写入Channel 140 | - [go\Time::sleep](https://github.com/birdwyx/phpgo/blob/master/md/cn/time-sleep.md) — 协程挂起一段时间(单位纳秒),如果在协程外调用,则调度线程挂起相应时长 141 | - [go\Time::tick](https://github.com/birdwyx/phpgo/blob/master/md/cn/time-tick.md) — 创建并返回一个Channel,phpgo周期性地、以指定纳秒时间间隔将当时的精确时间写入Channel 142 | 143 | ## [go\Runtime](https://github.com/birdwyx/phpgo/blob/master/md/cn/runtime.md) — Runtime类 144 | - [go\Runtime::numGoroutine](https://github.com/birdwyx/phpgo/blob/master/md/cn/runtime-numGoroutine.md) — 返回当前调度器管辖下的未运行完毕的协程数 145 | - [go\Runtime::gosched](https://github.com/birdwyx/phpgo/blob/master/md/cn/runtime-gosched.md) — 在协程中调用,则本协程阻塞;在协程外调用,则运行一次协程调度器 146 | - [go\Runtime::goid](https://github.com/birdwyx/phpgo/blob/master/md/cn/runtime-goid.md) — 返回当前协程id,若在携程外调用,返回0 147 | -------------------------------------------------------------------------------- /REFERENCE_EN.md: -------------------------------------------------------------------------------- 1 | The phpgo referecne manual will be updated ,,, very soon! 2 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension phpgo 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | PHP_ARG_WITH(phpgo, for phpgo support, 11 | [ --with-phpgo Include phpgo support]) 12 | 13 | dnl Otherwise use enable: 14 | 15 | PHP_ARG_ENABLE(phpgo, whether to enable phpgo support, 16 | [ --enable-phpgo Enable phpgo support]) 17 | 18 | if test "$PHP_PHPGO" != "no"; then 19 | dnl Write more examples of tests here... 20 | 21 | dnl # --with-phpgo -> check with-path 22 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 23 | dnl SEARCH_FOR="/include/phpgo.h" # you most likely want to change this 24 | dnl if test -r $PHP_PHPGO/$SEARCH_FOR; then # path given as parameter 25 | dnl PHPGO_DIR=$PHP_PHPGO 26 | dnl else # search default path list 27 | dnl AC_MSG_CHECKING([for phpgo files in default path]) 28 | dnl for i in $SEARCH_PATH ; do 29 | dnl if test -r $i/$SEARCH_FOR; then 30 | dnl PHPGO_DIR=$i 31 | dnl AC_MSG_RESULT(found in $i) 32 | dnl fi 33 | dnl done 34 | dnl fi 35 | dnl 36 | dnl if test -z "$PHPGO_DIR"; then 37 | dnl AC_MSG_RESULT([not found]) 38 | dnl AC_MSG_ERROR([Please reinstall the phpgo distribution]) 39 | dnl fi 40 | 41 | dnl # --with-phpgo -> add include path 42 | dnl PHP_ADD_INCLUDE($PHPGO_DIR/include) 43 | 44 | dnl # --with-phpgo -> check for lib and symbol presence 45 | dnl LIBNAME=phpgo # you may want to change this 46 | dnl LIBSYMBOL=phpgo # you most likely want to change this 47 | 48 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 49 | dnl [ 50 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $PHPGO_DIR/$PHP_LIBDIR, PHPGO_SHARED_LIBADD) 51 | dnl AC_DEFINE(HAVE_PHPGOLIB,1,[ ]) 52 | dnl ],[ 53 | dnl AC_MSG_ERROR([wrong phpgo lib version or lib not found]) 54 | dnl ],[ 55 | dnl -L$PHPGO_DIR/$PHP_LIBDIR -lm 56 | dnl ]) 57 | dnl 58 | dnl PHP_SUBST(PHPGO_SHARED_LIBADD) 59 | 60 | dnl with split-stack, turn on the following(seems not working) 61 | dnl CXXFLAGS="-std=c++11 -fsplit-stack $CXXFLAGS" 62 | dnl otherwise turn on the following: 63 | CXXFLAGS="-std=c++11 $CXXFLAGS" 64 | 65 | PHP_SUBST(PHPGO_SHARED_LIBADD) 66 | PHP_REQUIRE_CXX() 67 | PHP_ADD_LIBRARY(libgo, 1, PHPGO_SHARED_LIBADD) 68 | dnl PHP_ADD_LIBRARY(libdl, 1, PHPGO_SHARED_LIBADD) 69 | dnl PHP_ADD_LIBRARY(boost_thread, 1, PHPGO_SHARED_LIBADD) 70 | PHP_ADD_LIBRARY(stdc++, 1, PHPGO_SHARED_LIBADD) 71 | PHP_NEW_EXTENSION(phpgo, src/phpgo.cc src/phpgo_context.cc src/go.cc src/go_chan.cc src/go_runtime.cc src/go_time.cc src/go_select.cc src/zend_variables_persist.cc src/task_local_storage.cc src/basic_functions.cc, $ext_shared) 72 | fi 73 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("phpgo", "for phpgo support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("phpgo", "enable phpgo support", "no"); 9 | 10 | if (PHP_PHPGO != "no") { 11 | EXTENSION("phpgo", "phpgo.c"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /docker/CentOS7-Base-163.repo: -------------------------------------------------------------------------------- 1 | # CentOS-Base.repo 2 | # 3 | # The mirror system uses the connecting IP address of the client and the 4 | # update status of each mirror to pick mirrors that are updated to and 5 | # geographically close to the client. You should use this for CentOS updates 6 | # unless you are manually picking other mirrors. 7 | # 8 | # If the mirrorlist= does not work for you, as a fall back you can try the 9 | # remarked out baseurl= line instead. 10 | # 11 | # 12 | [base] 13 | name=CentOS-$releasever - Base - 163.com 14 | #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os 15 | baseurl=http://mirrors.163.com/centos/$releasever/os/$basearch/ 16 | gpgcheck=1 17 | gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 18 | 19 | #released updates 20 | [updates] 21 | name=CentOS-$releasever - Updates - 163.com 22 | #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates 23 | baseurl=http://mirrors.163.com/centos/$releasever/updates/$basearch/ 24 | gpgcheck=1 25 | gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 26 | 27 | #additional packages that may be useful 28 | [extras] 29 | name=CentOS-$releasever - Extras - 163.com 30 | #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras 31 | baseurl=http://mirrors.163.com/centos/$releasever/extras/$basearch/ 32 | gpgcheck=1 33 | gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 34 | 35 | #additional packages that extend functionality of existing packages 36 | [centosplus] 37 | name=CentOS-$releasever - Plus - 163.com 38 | baseurl=http://mirrors.163.com/centos/$releasever/centosplus/$basearch/ 39 | gpgcheck=1 40 | enabled=0 41 | gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7 42 | -------------------------------------------------------------------------------- /docker/build.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DKNAME="registry.cn-hangzhou.aliyuncs.com/baipeng/php" 3 | TAG="5.6.31-fpm" 4 | docker build --pull -t $DKNAME:$TAG . 5 | -------------------------------------------------------------------------------- /docker/php-fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | 3 | daemonize = no 4 | 5 | pid = /usr/local/php/var/run/php-fpm.pid 6 | 7 | error_log = /usr/local/php/var/log/php-fpm.log 8 | 9 | log_level = notice 10 | 11 | [www] 12 | 13 | listen = 0.0.0.0:9000 14 | 15 | listen.backlog = 2048 16 | ;允许访问FastCGI进程的IP白名单,设置any为不限制IP 17 | ;listen.allowed_clients = any 18 | listen.owner = www 19 | listen.group = www 20 | listen.mode = 0666 21 | user = www 22 | group = www 23 | ;php-fpm进程启动模式,pm可以设置为static和dynamic和ondemand 24 | pm = dynamic 25 | ;如果选择static,则进程数就数固定的,由pm.max_children指定固定的子进程数。 26 | ;如果选择dynamic,则进程数是动态变化的,由以下参数决定: 27 | ;子进程最大数 28 | pm.max_children = 70 29 | ;启动时的进程数,默认值为: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 30 | pm.start_servers = 8 31 | ;保证空闲进程数最小值,如果空闲进程小于此值,则创建新的子进程 32 | pm.min_spare_servers = 6 33 | pm.max_spare_servers = 10 34 | 35 | ;设置每个子进程重生之前服务的请求数 36 | pm.max_requests=1000 37 | 38 | ;FPM监控页面的ping网址. 如果没有设置, 则无法访问ping页面. 该页面用于外部检测FPM是否存活并且可以响应请求. 请注意必须以斜线开头 (/)。 39 | ping.path = /ping 40 | ;用于定义ping请求的返回相应. 返回为 HTTP 200 的 text/plain 格式文本. 默认值: pong. 41 | ping.response = server:9000 42 | request_terminate_timeout = 100 43 | request_slowlog_timeout = 0 44 | slowlog = /usr/local/php/var/log/slow.log 45 | ;启用php-fpm状态功能 46 | pm.status_path = /status 47 | -------------------------------------------------------------------------------- /examples/chan_greetings.php: -------------------------------------------------------------------------------- 1 | push("Greeting from Alice\n"); 9 | }); 10 | 11 | $message = $ch->pop(); 12 | echo $message; 13 | -------------------------------------------------------------------------------- /examples/chan_read_write_closed_chan.php: -------------------------------------------------------------------------------- 1 | pop(); 9 | echo "data read in go 1:"; var_dump($data); 10 | }); 11 | 12 | go(function() use($ch){ 13 | sleep(1); 14 | echo "go 2 close channel\n"; 15 | $data = $ch->close(); 16 | }); 17 | 18 | Scheduler::join(); 19 | -------------------------------------------------------------------------------- /examples/chan_trypop_simulate_pop.php: -------------------------------------------------------------------------------- 1 | push("Greeting from Alice\n"); 9 | }); 10 | 11 | while(!($message=$ch->tryPop())) Scheduler::run(); 12 | echo $message; 13 | -------------------------------------------------------------------------------- /examples/chan_trypush_simulate_push.php: -------------------------------------------------------------------------------- 1 | tryPush("Greeting from Alice\n"); 10 | if($ok || $ok===NULL) break; 11 | Runtime::Gosched(); 12 | } 13 | }); 14 | 15 | $message=$ch->pop(); 16 | echo $message; 17 | -------------------------------------------------------------------------------- /examples/select_block.phpt: -------------------------------------------------------------------------------- 1 | push("hello world\n"); 19 | 20 | Scheduler::join(); -------------------------------------------------------------------------------- /examples/select_producer_consumer.php: -------------------------------------------------------------------------------- 1 | close(); 17 | } 18 | ], 19 | [ 20 | 'default', function(){ 21 | echo "channel full, wait 50ms for consumer to consume\n"; 22 | Time::sleep(50*1000*1000); 23 | } 24 | ] 25 | )->loop($data_written); 26 | } 27 | 28 | function producer($ch, $close){ 29 | select( 30 | [ 31 | 'default', function() use($ch){ 32 | $data = rand(); 33 | write_data($ch, $data); 34 | } 35 | ] 36 | )->loop($close); 37 | } 38 | 39 | function consumer($ch, $close){ 40 | select( 41 | [ 42 | 'case', $ch, function($value) { 43 | echo "data consumed:"; 44 | var_dump($value); 45 | 46 | // wait a 10ms: simulating a slow consumer: 47 | // just to see the output from producer: 48 | // "channel full, wait 50ms for consumer to consume" 49 | Time::sleep(10*1000*1000); 50 | } 51 | ] 52 | )->loop($close); 53 | } 54 | 55 | // main routine 56 | go( function(){ 57 | $ch = new Chan(["capacity"=>20]); 58 | $close = new Chan(0); 59 | go('producer', [$ch, $close]); 60 | go('consumer', [$ch, $close]); 61 | 62 | // tell producer and consumer to exit after 10 seconds 63 | // note: Time::sleep follows go convension: time unit in nano-second 64 | // please note the difference with php sleep() witch has time unit in seconds 65 | Time::sleep(10* 1000*1000*1000); 66 | $close->close(); 67 | }); 68 | 69 | Scheduler::join(); -------------------------------------------------------------------------------- /examples/select_read_default.php: -------------------------------------------------------------------------------- 1 | 1]); 6 | 7 | go(function() use($ch){ 8 | $read = false; $df = false; $v=0; 9 | $ch->push(1); 10 | select( 11 | [ 12 | 'case', $ch, "->", &$v, function($value) use(&$read){ 13 | //$value (==$v) should be 1 as read from $ch 14 | if($value===1) 15 | $read = true; 16 | } 17 | ], 18 | [ 19 | 'default', function() use(&$df){ 20 | $df = true; 21 | } 22 | ] 23 | ); 24 | 25 | assert( 26 | $read===true && //should hit the channel-read branch 27 | $df===false && //should not hit the default branch 28 | $v === 1 //$v should be 1 as the popped data from channel is assigned to its reference 29 | ); 30 | echo "success\n"; 31 | }); 32 | 33 | Scheduler::join(); 34 | -------------------------------------------------------------------------------- /examples/selector_loop.php: -------------------------------------------------------------------------------- 1 | 1]); 7 | $ch2 = new Chan(["capacity"=>1]); 8 | $exit = new Chan(0); 9 | 10 | go(function() use($ch1, $ch2, $exit){ 11 | select( 12 | [ 13 | 'case', $ch1, function($value){ 14 | echo "go 0: receive from ch1: " . $value . PHP_EOL; 15 | } 16 | ], 17 | [ 18 | 'case', $ch2, function($value){ 19 | echo "go 0: receive from ch2: " . $value . PHP_EOL; 20 | } 21 | ] 22 | )->loop($exit); 23 | }); 24 | 25 | go(function() use($ch1, $exit){ 26 | $i = 0; 27 | while( $exit->tryPop() !== NULL ){ //===NULL: channel closed 28 | echo "go 1: push $i\n"; 29 | $ch1->push($i++); 30 | } 31 | }); 32 | 33 | go(function() use($ch2, $exit){ 34 | $i = 0; 35 | while( $exit->tryPop() !== NULL ){ 36 | echo "go 2: push $i\n"; 37 | $ch2->push($i++); 38 | } 39 | }); 40 | 41 | go(function() use($exit){ 42 | echo "main: now sleep 5ms\n"; 43 | Time::sleep(5*1000*1000); 44 | echo "main: now close the exit channel\n"; 45 | $exit->close(); 46 | }); 47 | 48 | Scheduler::join(); 49 | -------------------------------------------------------------------------------- /examples/selector_select.php: -------------------------------------------------------------------------------- 1 | 1]); 7 | $ch2 = new Chan(["capacity"=>1]); 8 | $exit = new Chan(0); 9 | 10 | go(function() use($ch1, $ch2, $exit){ 11 | $selector = select( 12 | [ 13 | 'case', $ch1, function($value){ 14 | echo "go 0: receive from ch1: " . $value . PHP_EOL; 15 | } 16 | ], 17 | [ 18 | 'case', $ch2, function($value){ 19 | echo "go 0: receive from ch2: " . $value . PHP_EOL; 20 | } 21 | ] 22 | ); 23 | 24 | echo "go 0: after 1st select\n"; 25 | 26 | while( $exit->tryPop() !== NULL ) $selector->select(); 27 | }); 28 | 29 | go(function() use($ch1, $exit){ 30 | $i = 0; 31 | while( $exit->tryPop() !== NULL ){ //===NULL: channel closed 32 | echo "go 1: push $i\n"; 33 | $ch1->push($i++); 34 | } 35 | }); 36 | 37 | go(function() use($ch2, $exit){ 38 | $i = 0; 39 | while( $exit->tryPop() !== NULL ){ 40 | echo "go 2: push $i\n"; 41 | $ch2->push($i++); 42 | } 43 | }); 44 | 45 | go(function() use($exit){ 46 | echo "main: now sleep 5ms\n"; 47 | Time::sleep(5*1000*1000); 48 | echo "main: now close the exit channel\n"; 49 | $exit->close(); 50 | }); 51 | 52 | Scheduler::join(); 53 | -------------------------------------------------------------------------------- /md/cn/chan-close.md: -------------------------------------------------------------------------------- 1 | # go\Chan::close 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | close — 关闭管道 6 | 7 | ## 说明 8 | #### bool close() 9 | 10 | 管道关闭之后,资源并不立即释放,管道资源将在最后一个使用者退出时释放。 11 | 12 | >读写关闭的管道: 13 | >从已关闭管道读取数据时,如果管道内还有数据,则返回该数据,如果没有数据则返回NULL; 14 | >向关闭的管道写入数据会失败并返回FALSE。 15 | >注:关闭管道时,已经在管道上阻塞等待的push()和pop()调用并不会返回。这与golang的行为是不同的:golang中一旦关闭管道,所有在管道读写的go routine都会立即返回。 16 | >之所以有这个限制的原因是目前libgo正式版本尚未支持管道关闭操作,而phpgo目前并不希望修改libgo并维护一个私有的libgo版本。 17 | >如果想在管道关闭时可以退出,可以使用非阻塞调用tryPush/tryPop或select来代替push/pop。 18 | 19 | ## 返回值 20 | 成功则返回TRUE,失败则返回FALSE。 21 | 22 | ## 示例 23 | ### 1. 读写关闭的管道 24 | ``` 25 | pop(); 33 | echo "data read in go 1:"; var_dump($data); 34 | }); 35 | 36 | go(function() use($ch){ 37 | sleep(1); 38 | echo "go 2 close channel\n"; 39 | $data = $ch->close(); 40 | }); 41 | 42 | Scheduler::join(); 43 | ``` 44 | 输出go 2 close channel之后程序挂起: 45 | ``` 46 | go 1 try to read 47 | go 2 close channel 48 | ``` 49 | -------------------------------------------------------------------------------- /md/cn/chan-construct.md: -------------------------------------------------------------------------------- 1 | # go\Chan::__construct 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | __construct — 创建管道 6 | 7 | ## 说明 8 | #### __construct( [array $create_opptions | int $capacity] ) 9 | 10 | ## 参数 11 | #### create_opptions 12 | 13 | 当__construct第一个参数为数组时,代表传入的是创建参数。例如: 14 | ``` 15 | $create_opptions = array( 16 | 'name' => 'chanName', //管道名称 17 | 'copy' => true, //是否复制 18 | 'capacity' => 10, //最大容量 19 | ); 20 | 21 | $ch = new Chan($create_opptions); 22 | ``` 23 | 管道名称:可以命名一个管道,通过相同的管道名称可以获取同一个管道对象。非首次调用时copy/capacity不起作用 24 | 是否复制:为true时压入管道的数据将被复制到共享内存块中,一般在需要线程之间传递数据时才设为true 25 | 最大容量:管道中最多容纳的数据项个数 26 | 27 | #### capacity 28 | 29 | 当__construct第一个参数为整数时,代表最大容量。例如以下语句创建容量为10的管道: 30 | ```` 31 | $ch = new Chan(10); 32 | ```` 33 | 34 | ## 返回值 35 | 返回创建的管道对象 36 | -------------------------------------------------------------------------------- /md/cn/chan-destruct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/chan-destruct.md -------------------------------------------------------------------------------- /md/cn/chan-pop.md: -------------------------------------------------------------------------------- 1 | # go\Chan::pop 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | pop — 从管道头部读取一条数据 6 | 7 | ## 说明 8 | #### mixed pop() 9 | 10 | ## 返回值 11 | 成功则返回读取的数据,如果没有数据则阻塞。 12 | 如果管道关闭则返回NULL,如果出错则返回FALSE。 13 | 14 | ## 参见 15 | - [go\Chan::push](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-push.md) — 在管道尾部添加一条数据 16 | - [go\Chan::tryPop](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-trypop.md) — 从管道头部读取一条数据,不阻塞 17 | 18 | ## 示例 19 | ### 1. 使用channel和go routine通信 20 | ``` 21 | push("Greeting from Alice\n"); 29 | }); 30 | 31 | $message = $ch->pop(); 32 | echo $message; 33 | ``` 34 | 输出: 35 | ``` 36 | Alice sends a greeting 37 | Greeting from Alice 38 | ``` 39 | -------------------------------------------------------------------------------- /md/cn/chan-push.md: -------------------------------------------------------------------------------- 1 | # go\Chan::push 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | push — 在管道尾部添加一条数据 6 | 7 | ## 说明 8 | #### bool push ( mixed $data ) 9 | 在管道尾部添加一条数据 10 | 11 | ## 参数 12 | #### data 13 | 14 | 要向channel写入的数据 15 | 16 | ## 返回值 17 | 成功返回TURE; 18 | 如果管道已满则阻塞; 19 | 如果管道关闭则返回NULL; 20 | 出错则返回FALSE。 21 | 22 | ## 参见 23 | - [go\Chan::tryPush](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-trypush.md) — 尝试在管道尾部添加一条数据,不阻塞 24 | - [go\Chan::pop](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-pop.md) — 从管道头部读取一条数据 25 | 26 | ## 示例 27 | ### 1. 使用channel和go routine通信 28 | ``` 29 | push("Greeting from Alice\n"); 37 | }); 38 | 39 | $message = $ch->pop(); 40 | echo $message; 41 | ``` 42 | 输出: 43 | ``` 44 | Alice sends a greeting 45 | Greeting from Alice 46 | ``` 47 | -------------------------------------------------------------------------------- /md/cn/chan-trypop.md: -------------------------------------------------------------------------------- 1 | # go\Chan::tryPop 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | tryPop — 尝试从管道头部读取一条数据 6 | 7 | ## 说明 8 | #### mixed tryPop() 9 | 10 | ## 返回值 11 | 尝试从管道头部读取一条数据。如果管道关闭则返回NULL,如果没有数据或出错则返回FALSE。 12 | 13 | ## 参见 14 | - [go\Chan::push](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-push.md) — 在管道尾部添加一条数据 15 | - [go\Chan::pop](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-trypop.md) — 从管道头部读取一条数据 16 | 17 | ## 示例 18 | ### 1. 模拟pop 19 | ``` 20 | push("Greeting from Alice\n"); 28 | }); 29 | 30 | while(!($message=$ch->tryPop())) Scheduler::run(); 31 | echo $message; 32 | ``` 33 | 输出: 34 | ``` 35 | Alice sends a greeting 36 | Greeting from Alice 37 | ``` 38 | -------------------------------------------------------------------------------- /md/cn/chan-trypush.md: -------------------------------------------------------------------------------- 1 | # go\Chan::tryPush 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | tryPush — 尝试在管道尾部添加一条数据 6 | 7 | ## 说明 8 | #### mixed tryPush() 9 | 尝试在管道尾部添加一条数据 10 | 11 | ## 返回值 12 | 如果成功则返回TRUE,如果管道已满或出错则返回FALSE,如果管道关闭则返回NULL 13 | 14 | ## 参见 15 | - [go\Chan::push](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-push.md) — 在管道尾部添加一条数据 16 | - [go\Chan::pop](https://github.com/birdwyx/phpgo/blob/master/md/cn/chan-trypop.md) — 从管道头部读取一条数据 17 | 18 | ## 示例 19 | ### 1. 模拟push 20 | ``` 21 | tryPush("Greeting from Alice\n"); 30 | if($ok || $ok===NULL) break; 31 | Runtime::Gosched(); 32 | } 33 | }); 34 | 35 | $message=$ch->pop(); 36 | echo $message; 37 | ``` 38 | 输出: 39 | ``` 40 | Alice sends a greeting 41 | Greeting from Alice 42 | ``` 43 | -------------------------------------------------------------------------------- /md/cn/chan.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/chan.md -------------------------------------------------------------------------------- /md/cn/go.md: -------------------------------------------------------------------------------- 1 | # go 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | go — 创建协程(go routine) 6 | 7 | ## 说明 8 | #### bool go ( callable $callback \[, array $parameters\] ) 9 | 10 | ## 参数 11 | #### callback 12 | 13 | mixed callback (\[mixed $parameter1, \[ mixed $parameter2, ...\] \]) 14 | 15 | 回调函数,也就是go routine的主函数,go routine的主函数在协程创建后立即执行。 16 | 17 | >go 的回调函数参数可以是: 18 | >- 匿名函数,如: 19 | >``` 20 | >go(function(){ 21 | > echo "Hello World!\n"; 22 | >}); 23 | >``` 24 | >- 普通函数,如: 25 | >``` 26 | >go('hello', ['World']); //相当于在协程里执行hello('World'); 27 | >``` 28 | >- 类的静态方法,如: 29 | >``` 30 | >go('ClassA::hello', ['phpgo']); //相当于在协程里执行 ClassA::Hello('phpgo'); 31 | >go(["ClassA", "hello"], ['phpgo']); //相当于在协程里执行 ClassA::Hello('phpgo'); 32 | >``` 33 | >- 类的非静态方法,如: 34 | >``` 35 | >go([$obj, 'hello'], ['phpgo']); //相当于在协程里执行 $obj->hello('phpgo'); 36 | >``` 37 | 38 | #### parameters 39 | 40 | 传递给go routine主函数(callback)的参数数组。 41 | 42 | $parameters数组中的每个参数与$callback参数表里的参数依次匹配。 43 | 44 | 如果在callback参数表里定义了引用传参方式,则$parameters数组里对应参数必须为引用,否则在callback里对该引用的修改对外部参数不起作用。 45 | 46 | ## 返回值 47 | 成功返回TURE,失败返回FALSE。 48 | 49 | ## 参见 50 | - [goo](https://github.com/birdwyx/phpgo/blob/master/md/cn/goo.md) — 创建go routine,支持可选参数 51 | 52 | ## 示例 53 | ### 1. Hello World 54 | ``` 55 | push("Greeting from Alice\n"); 119 | }); 120 | 121 | $message = $ch->pop(); 122 | echo $message; 123 | ``` 124 | 输出: 125 | ``` 126 | Alice sends a greeting 127 | Greeting from Alice 128 | ``` 129 | 上面这个示例展示了使用channel来与go routine通信的方法,也展示了可以使用use关键字结合匿名函数来向go routine传参,一般来说仅使用use来传递只读参数,如果在use里传入引用并在go routine里对相应参数进行更改,则与上面示例3一样存在耦合问题。 130 | 131 | 关于channel的使用,参见 [go\Chan](https://github.com/birdwyx/phpgo/md/cn/chan.md) 132 | 133 | ### 5. 使用命名函数 134 | ``` 135 | = 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | goo — 创建协程(go routine), 可以设置协程参数 6 | 7 | ## 说明 8 | #### bool goo (array $options, callable $callback \[, array $parameters\] ) 9 | 10 | ## 参数 11 | #### options 12 | 13 | 协程参数数组: 以key value对形式的协程参数组成的数组。 14 | 15 | 可以设置以下协程参数: 16 | 17 | ##### stack_size 18 | 创建协程的最大栈大小(bytes),默认1024M, stack_size不能小于32M;如果指定了小于32M的值,phpgo会自动设置为32M。 19 | 事实上协程栈是自动增长的,使用比较大的值也不会造成内存浪费。这个参数一般不用设置。 20 | 21 | ##### isolate_http_globals 22 | 是否将子协程和父协程的超全局变量分离,当isolate_http_globals为true时,则将子协程使用自己独立的超全局变量。 23 | 24 | 当子协程修改了超全局变量的值时不会影响父协程和其他协程里超全局变量的值。 25 | 26 | 这里提到的超全局变量指: 27 | 28 | $_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_REQUEST, $_FILES 29 | 30 | 注意这里不包含$GLOBALS超全局变量。在任何地方修改$GLOBALS都会影响到协程外(调度器)或其他协程看到的值。 31 | 32 | >#### isolate_http_globals 有什么用呢? 33 | > 34 | >最大的用处是在你用多个协程来处理http的请求的时候,还可以调用一些会使用/修改超全局变量的现有方法, 这在你移植一些老代码到phpgo上的时候会有用。如果没有isolate_http_globals,在协程里你是不能随便修改这些超全局变量的,因为这会污染到协程外或其他协程内的超全局变量, 35 | 而你的老代码是肯定不会料到后面还会出现协程这回事,当然不会考虑这种影响。通过isolate_http_globals=true 你就可以放心了。 36 | 37 | #### callback 38 | 39 | mixed callback (\[mixed $parameter1, \[ mixed $parameter2, ...\] \]) 40 | 41 | 回调函数,也就是go routine的主函数,go routine的主函数在协程创建后立即执行。 42 | 43 | #### parameters 44 | 45 | 传递给go routine主函数(callback)的参数数组。 46 | 47 | $parameters数组中的每个参数与$callback参数表里的参数依次匹配。 48 | 49 | 如果在callback参数表里定义了引用传参方式,则$parameters数组里对应参数必须为引用,否则在callback里对该引用的修改对外部参数不起作用。 50 | 51 | ## 返回值 52 | 成功返回TURE,失败返回FALSE。 53 | 54 | ## 参见 55 | - [go](https://github.com/birdwyx/phpgo/blob/master/md/cn/go.md) — 创建go routine 56 | 57 | ## 示例 58 | ``` 59 | 64 * 1024, 68 | 'isolate_http_globals' => true 69 | ]; 70 | 71 | $ch = new Chan(1); 72 | goo($options, function() use ($get, $ch) { 73 | if($get == $_GET) echo 'go routine correctly inherits $_GET' . PHP_EOL; 74 | $_GET['var'] = 'world'; 75 | $ch->push(1); 76 | }); 77 | $ch->pop(); // wait the go routine to finish 78 | 79 | if($_GET['var'] == 'hello') echo 'change of $_GET in go routine does not impact the ouside' . PHP_EOL; 80 | 81 | ``` 82 | 输出 83 | ``` 84 | go routine correctly inherits $_GET 85 | change of $_GET in go routine does not impact the ouside 86 | ``` 87 | -------------------------------------------------------------------------------- /md/cn/mutex-construct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/mutex-construct.md -------------------------------------------------------------------------------- /md/cn/mutex-destruct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/mutex-destruct.md -------------------------------------------------------------------------------- /md/cn/mutex-islock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/mutex-islock.md -------------------------------------------------------------------------------- /md/cn/mutex-lock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/mutex-lock.md -------------------------------------------------------------------------------- /md/cn/mutex-trylock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/mutex-trylock.md -------------------------------------------------------------------------------- /md/cn/mutex-unlock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/mutex-unlock.md -------------------------------------------------------------------------------- /md/cn/mutex.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/mutex.md -------------------------------------------------------------------------------- /md/cn/runtime-goid.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/runtime-goid.md -------------------------------------------------------------------------------- /md/cn/runtime-gosched.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/runtime-gosched.md -------------------------------------------------------------------------------- /md/cn/runtime-numGoroutine.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/runtime-numGoroutine.md -------------------------------------------------------------------------------- /md/cn/runtime.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/runtime.md -------------------------------------------------------------------------------- /md/cn/scheduler-join.md: -------------------------------------------------------------------------------- 1 | # go\Scheduler::join 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | join — 运行协程调度器直到所用户协程数小于指定数量 6 | 7 | ## 说明 8 | #### mixed join([int $remains]) 9 | 10 | 运行协程调度器,等待所有用户协程运行完毕后返回;若指定了remains参数,则当前用户协程数小于等于remains时返回。 11 | 注意这里的“用户协程”指通过在php代码调用go/goo启动的协程;phpgo内部也可能会启动协程,这些协程不算在内。 12 | 13 | ## 参数 14 | 15 | #### remains 16 | join()在用户协程数减小到remains时返回 17 | 18 | 19 | ## 返回值 20 | 返回TRUE。 21 | 22 | ## 参见 23 | - [go\Scheduler::loop](https://github.com/birdwyx/phpgo/blob/master/md/cn/scheduler-loop.md) — 运行协程调度器直到调度线程退出 24 | - [go\Scheduler::run](https://github.com/birdwyx/phpgo/blob/master/md/cn/scheduler-run.md) — 检查一遍所有协程,如果有就绪的就运行一次 25 | -------------------------------------------------------------------------------- /md/cn/scheduler-loop.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /md/cn/scheduler-run.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/scheduler-run.md -------------------------------------------------------------------------------- /md/cn/scheduler.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/scheduler.md -------------------------------------------------------------------------------- /md/cn/select.md: -------------------------------------------------------------------------------- 1 | # select 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | select — 进行一次事件轮询,并返回一个Selector对象 6 | 7 | 和golang的select语法结构类似, phpgo select轮询所有指定的分支,随机选择一个可读写(即,channel可读或可写)的分支,执行对应读写操作后,再执行该分支对应的回调函数,并传入所读到/写入的数据。 8 | 9 | 如果没有可读写分支,select执行'default'分支对应的回调函数。如果连'default'分支都没有,则select阻塞一直到有读写操作可进行。 10 | 11 | ## 说明 12 | #### Selector select ( array $case1, [ array $case2, ...] ) 13 | 14 | ## 参数 15 | #### case 16 | 一个case代表select事件轮询的一个分支,每个case是一个数组,组成如下: 17 | ##### array( string $switch_type, \[ Chan $channel, string $operator, mixed $value, \] Callable $callback ) 18 | 19 | 其中 switch_type 可以以下两者之一: 20 | 21 | - 'case',普通分支,类似golang中的case关键字所代表的的分支。 22 | 23 | - 'default',默认分支,类似golang中的default关键字所代表的的分支。 24 | 25 | switch_type大小写不敏感。 26 | 27 | #### 'case'分支 28 | 语法如下: 29 | ##### array('case', Chan $channel, \['->', \[mixed &$value\]\] | \['<-', mixed $value\], Callable $callback) 30 | 其中: 31 | '->'操作符表示从通道$channel中读取数据到$value中,然后调用$callback($value), $value省略时,则从$channel读取数据到临时变量,并传入$callback中。$value不省略时,必须是引用参数。 32 | '<-'操作符表示将$value写入到$channel中,写入后调用$callback($value)。此时$value不能省略。 33 | '<-','->'和$value都省略时,默认为读操作,即从$channel读取数据到临时变量,并传入$callback中。 34 | 35 | >也就是说,总共有以下几种合法情形: 36 | >``` 37 | >select( ['case', $ch, '->', &$value, function($v){...} ], [...] ); //从$ch读取数据到$value中,并传给function 38 | >select( ['case', $ch, '->', function($v){...} ], [...] ); //从$ch读取数据到临时变量,并传给function 39 | >select( ['case', $ch, function($v){...} ], [...] ); //从$ch读取数据到临时变量,并传给function 40 | > 41 | >select( ['case', $ch, '<-', $value, function($v){...} ], [...] ); //将$value写到$ch中,然后将$value传给function 42 | >``` 43 | 44 | #### 'default'分支 45 | 语法如下: 46 | ##### array('default', Callable $callback) 47 | select轮询所有分支,随机选择一个可读写的'case'分支来执行,如果所有'case'分支都不可读或写,则select执行'default'分支的回调函数$callback,不传入参数。 48 | >例如,以下案例在各个case分支没有读写操作可进行的时候,等待一秒钟: 49 | >``` 50 | >select( 51 | > ['case', ...], 52 | > ['case', ...], 53 | > ['default', function(){ sleep(1); }] 54 | >); 55 | >``` 56 | 57 | ## 返回值 58 | select 分析传入的分支参数,生成内部的Selector对象,将分支信息存入该对象,然后返回该Selector对象。 59 | >使用Selector对象可以优化性能,因为不用每次都解析select的参数了,可以节省CPU资源。 60 | 61 | ## 参见 62 | - [go\Selector](https://github.com/birdwyx/phpgo/blob/master/md/cn/selector.md) — Selector类 63 | 64 | ## 示例 65 | 66 | ### 1. Select读取数据 67 | ``` 68 | 1]); 73 | 74 | go(function() use($ch){ 75 | $read = false; $df = false; $v=0; 76 | $ch->push(1); 77 | select( 78 | [ 79 | 'case', $ch, "->", &$v, function($value) use(&$read){ 80 | //$value (==$v) should be 1 as read from $ch 81 | if($value===1) 82 | $read = true; 83 | } 84 | ], 85 | [ 86 | 'default', function() use(&$df){ 87 | $df = true; 88 | } 89 | ] 90 | ); 91 | 92 | assert( 93 | $read===true && //should hit the channel-read branch 94 | $df===false && //should not hit the default branch 95 | $v === 1 //$v should be 1 as the popped data from channel is assigned to its reference 96 | ); 97 | echo "success\n"; 98 | }); 99 | 100 | Scheduler::join(); 101 | ``` 102 | 输出success,无断言失败: 103 | ``` 104 | success 105 | ``` 106 | 107 | 108 | ### 2. 生产者与消费者 109 | ``` 110 | close(); 126 | } 127 | ], 128 | [ 129 | 'default', function(){ 130 | echo "channel full, wait 50ms for consumer to consume\n"; 131 | Time::sleep(50*1000*1000); 132 | } 133 | ] 134 | )->loop($data_written); 135 | } 136 | 137 | function producer($ch, $close){ 138 | select( 139 | [ 140 | 'default', function() use($ch){ 141 | $data = rand(); 142 | write_data($ch, $data); 143 | } 144 | ] 145 | )->loop($close); 146 | } 147 | 148 | function consumer($ch, $close){ 149 | select( 150 | [ 151 | 'case', $ch, function($value) { 152 | echo "data consumed:"; 153 | var_dump($value); 154 | 155 | // wait a 10ms: simulating a slow consumer: 156 | // just to see the output from producer: 157 | // "channel full, wait 50ms for consumer to consume" 158 | Time::sleep(10*1000*1000); 159 | } 160 | ] 161 | )->loop($close); 162 | } 163 | 164 | // main routine 165 | go( function(){ 166 | $ch = new Chan(["capacity"=>20]); 167 | $close = new Chan(0); 168 | go('producer', [$ch, $close]); 169 | go('consumer', [$ch, $close]); 170 | 171 | // tell producer and consumer to exit after 10 seconds 172 | // note: Time::sleep follows go convension: time unit in nano-second 173 | // please note the difference with php sleep() witch has time unit in seconds 174 | Time::sleep(10* 1000*1000*1000); 175 | $close->close(); 176 | }); 177 | 178 | Scheduler::join(); 179 | ``` 180 | 输出: 181 | ``` 182 | ... 183 | data written:int(1848193587) 184 | data consumed:int(1848193587) 185 | data written:int(1572420815) 186 | data written:int(1142275719) 187 | data written:int(761289258) 188 | data written:int(251548635) 189 | data written:int(695961973) 190 | data written:int(1035384689) 191 | data written:int(1090100059) 192 | data written:int(220684829) 193 | data written:int(1971664342) 194 | data written:int(1672374281) 195 | data written:int(1560995315) 196 | data written:int(1982880169) 197 | data written:int(1726045443) 198 | data written:int(390291502) 199 | data written:int(1979133962) 200 | data written:int(162498409) 201 | data written:int(1624140423) 202 | data written:int(856873465) 203 | data written:int(1547478199) 204 | data written:int(881099376) 205 | channel full, wait 50ms for consumer to consume 206 | data consumed:int(1572420815) 207 | data consumed:int(1142275719) 208 | data consumed:int(761289258) 209 | data consumed:int(251548635) 210 | data consumed:int(695961973) 211 | data written:int(215756702) 212 | data written:int(1076793154) 213 | data written:int(53161470) 214 | data written:int(591741440) 215 | data written:int(2144247704) 216 | channel full, wait 50ms for consumer to consume 217 | data consumed:int(1035384689) 218 | data consumed:int(1090100059) 219 | data consumed:int(220684829) 220 | data consumed:int(1971664342) 221 | data consumed:int(1672374281) 222 | data written:int(321058344) 223 | ... 224 | ``` 225 | 上例是经典的生产者消费者场景:生产者生产并写入数据,消费者读取并处理数据,以channel为缓冲区。 226 | - 主过程生成一个20缓冲项的channel,传给producer和consumer; 227 | - producer()生成随机数,并持续调用write_data将数据写入缓冲区$ch,直到收到退出信号; 228 | >如果缓冲区$ch满了,write_data会等50ms重试,一直到缓冲区有空了,成功写入数据才返回。 229 | - consumer()从$ch读取并打印出读取的值,直到收到退出信号。 230 | 231 | 退出信号即$close被关闭:select()返回一个Selector对象,通过调用Selector::loop($close)循环执行select中的case直到$close中有值或者$close被关闭,更多信息参见[Selector::Loop()](https://github.com/birdwyx/phpgo/blob/master/md/cn/selector-loop.md))。 232 | -------------------------------------------------------------------------------- /md/cn/selector-construct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/selector-construct.md -------------------------------------------------------------------------------- /md/cn/selector-loop.md: -------------------------------------------------------------------------------- 1 | # go\Selector::loop 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | loop — 持续进行事件轮询,直到指定channel可读或关闭 6 | 7 | ## 说明 8 | #### mixed loop(Chan $channel) 9 | 10 | 循环进行事件轮询,直到指定channel可读或关闭 11 | 12 | ## 返回值 13 | 如果轮询终止原因是指定channel可读,则返回读到的数据; 14 | 如果轮询终止原因是指定channel关闭,则返回NULL。 15 | 16 | ## 参见 17 | - [go\Selector::select](https://github.com/birdwyx/phpgo/blob/master/md/cn/selector-select.md) — 进行一次事件轮询 18 | 19 | ## 示例 20 | ### 1. loop常规用法 21 | ``` 22 | 1]); 28 | $ch2 = new Chan(["capacity"=>1]); 29 | $exit = new Chan(0); 30 | 31 | go(function() use($ch1, $ch2, $exit){ 32 | select( 33 | [ 34 | 'case', $ch1, function($value){ 35 | echo "go 0: receive from ch1: " . $value . PHP_EOL; 36 | } 37 | ], 38 | [ 39 | 'case', $ch2, function($value){ 40 | echo "go 0: receive from ch2: " . $value . PHP_EOL; 41 | } 42 | ] 43 | )->loop($exit); 44 | }); 45 | 46 | go(function() use($ch1, $exit){ 47 | $i = 0; 48 | while( $exit->tryPop() !== NULL ){ //===NULL: channel closed 49 | echo "go 1: push $i\n"; 50 | $ch1->push($i++); 51 | } 52 | }); 53 | 54 | go(function() use($ch2, $exit){ 55 | $i = 0; 56 | while( $exit->tryPop() !== NULL ){ 57 | echo "go 2: push $i\n"; 58 | $ch2->push($i++); 59 | } 60 | }); 61 | 62 | go(function() use($exit){ 63 | echo "main: now sleep 5ms\n"; 64 | Time::sleep(5*1000*1000); 65 | echo "main: now close the exit channel\n"; 66 | $exit->close(); 67 | }); 68 | 69 | Scheduler::join(); 70 | ``` 71 | 输出: 72 | ``` 73 | go 1: push 0 74 | go 1: push 1 75 | go 2: push 0 76 | go 2: push 1 77 | main: now sleep 5ms 78 | go 0: receive from ch1: 0 79 | go 0: receive from ch2: 0 80 | go 1: push 2 81 | go 2: push 2 82 | go 0: receive from ch2: 1 83 | go 0: receive from ch1: 1 84 | go 2: push 3 85 | go 1: push 3 86 | go 0: receive from ch1: 2 87 | go 0: receive from ch2: 2 88 | go 1: push 4 89 | go 2: push 4 90 | go 0: receive from ch1: 3 91 | go 0: receive from ch2: 3 92 | go 1: push 5 93 | go 2: push 5 94 | go 0: receive from ch1: 4 95 | go 0: receive from ch2: 4 96 | go 1: push 6 97 | go 2: push 6 98 | go 0: receive from ch2: 5 99 | go 0: receive from ch1: 5 100 | go 2: push 7 101 | go 1: push 7 102 | go 0: receive from ch2: 6 103 | go 0: receive from ch1: 6 104 | go 2: push 8 105 | go 1: push 8 106 | go 0: receive from ch1: 7 107 | go 0: receive from ch2: 7 108 | go 1: push 9 109 | go 2: push 9 110 | go 0: receive from ch1: 8 111 | go 0: receive from ch2: 8 112 | go 1: push 10 113 | go 2: push 10 114 | go 0: receive from ch2: 9 115 | go 0: receive from ch1: 9 116 | main: now close the exit channel 117 | go 0: receive from ch2: 10 118 | ``` 119 | -------------------------------------------------------------------------------- /md/cn/selector-select.md: -------------------------------------------------------------------------------- 1 | # go\Selector::select 2 | 3 | \(PHP 5 >= 5.4.0, PHP 7, phpgo >= 0.9.0\) 4 | 5 | select — 进行一次事件轮询,并返回selector对象本身 6 | 7 | ## 说明 8 | #### Selector select() 9 | 10 | go\Selector::select的行为与\select()行为一致。 11 | 12 | ## 返回值 13 | 返回selector对象本身 14 | 15 | ## 参见 16 | - [select](https://github.com/birdwyx/phpgo/blob/master/md/cn/select.md) — 进行一次事件轮询 17 | 18 | ## 示例 19 | ### 1. select常规用法 20 | ``` 21 | 1]); 27 | $ch2 = new Chan(["capacity"=>1]); 28 | $exit = new Chan(0); 29 | 30 | go(function() use($ch1, $ch2, $exit){ 31 | $selector = select( 32 | [ 33 | 'case', $ch1, function($value){ 34 | echo "go 0: receive from ch1: " . $value . PHP_EOL; 35 | } 36 | ], 37 | [ 38 | 'case', $ch2, function($value){ 39 | echo "go 0: receive from ch2: " . $value . PHP_EOL; 40 | } 41 | ] 42 | ); 43 | 44 | echo "go 0: after 1st select\n"; 45 | 46 | while( $exit->tryPop() !== NULL ) $selector->select(); 47 | }); 48 | 49 | go(function() use($ch1, $exit){ 50 | $i = 0; 51 | while( $exit->tryPop() !== NULL ){ //===NULL: channel closed 52 | echo "go 1: push $i\n"; 53 | $ch1->push($i++); 54 | } 55 | }); 56 | 57 | go(function() use($ch2, $exit){ 58 | $i = 0; 59 | while( $exit->tryPop() !== NULL ){ 60 | echo "go 2: push $i\n"; 61 | $ch2->push($i++); 62 | } 63 | }); 64 | 65 | go(function() use($exit){ 66 | echo "main: now sleep 5ms\n"; 67 | Time::sleep(5*1000*1000); 68 | echo "main: now close the exit channel\n"; 69 | $exit->close(); 70 | }); 71 | 72 | Scheduler::join(); 73 | ``` 74 | 输出: 75 | ``` 76 | go 1: push 0 77 | go 1: push 1 78 | go 2: push 0 79 | go 2: push 1 80 | main: now sleep 5ms 81 | go 0: receive from ch2: 0 82 | go 0: after 1st select 83 | go 0: receive from ch1: 0 84 | go 2: push 2 85 | go 1: push 2 86 | go 0: receive from ch1: 1 87 | go 0: receive from ch2: 1 88 | go 1: push 3 89 | go 2: push 3 90 | go 0: receive from ch1: 2 91 | go 0: receive from ch2: 2 92 | go 1: push 4 93 | go 2: push 4 94 | go 0: receive from ch2: 3 95 | go 0: receive from ch1: 3 96 | go 2: push 5 97 | go 1: push 5 98 | go 0: receive from ch2: 4 99 | go 0: receive from ch1: 4 100 | go 2: push 6 101 | go 1: push 6 102 | go 0: receive from ch2: 5 103 | go 0: receive from ch1: 5 104 | main: now close the exit channel 105 | go 0: receive from ch2: 6 106 | ``` 107 | -------------------------------------------------------------------------------- /md/cn/selector.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/selector.md -------------------------------------------------------------------------------- /md/cn/time-after.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/time-after.md -------------------------------------------------------------------------------- /md/cn/time-sleep.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/time-sleep.md -------------------------------------------------------------------------------- /md/cn/time-tick.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/time-tick.md -------------------------------------------------------------------------------- /md/cn/time.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/time.md -------------------------------------------------------------------------------- /md/cn/waitgroup-add.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/waitgroup-add.md -------------------------------------------------------------------------------- /md/cn/waitgroup-construct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/waitgroup-construct.md -------------------------------------------------------------------------------- /md/cn/waitgroup-destruct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/waitgroup-destruct.md -------------------------------------------------------------------------------- /md/cn/waitgroup-done.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/waitgroup-done.md -------------------------------------------------------------------------------- /md/cn/waitgroup-wait.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/waitgroup-wait.md -------------------------------------------------------------------------------- /md/cn/waitgroup.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/cn/waitgroup.md -------------------------------------------------------------------------------- /md/en/chan-close.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan-close.md -------------------------------------------------------------------------------- /md/en/chan-construct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan-construct.md -------------------------------------------------------------------------------- /md/en/chan-destruct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan-destruct.md -------------------------------------------------------------------------------- /md/en/chan-pop.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan-pop.md -------------------------------------------------------------------------------- /md/en/chan-push.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan-push.md -------------------------------------------------------------------------------- /md/en/chan-trypop.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan-trypop.md -------------------------------------------------------------------------------- /md/en/chan-trypush.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan-trypush.md -------------------------------------------------------------------------------- /md/en/chan.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/chan.md -------------------------------------------------------------------------------- /md/en/go.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/go.md -------------------------------------------------------------------------------- /md/en/mutex-construct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/mutex-construct.md -------------------------------------------------------------------------------- /md/en/mutex-destruct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/mutex-destruct.md -------------------------------------------------------------------------------- /md/en/mutex-islock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/mutex-islock.md -------------------------------------------------------------------------------- /md/en/mutex-lock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/mutex-lock.md -------------------------------------------------------------------------------- /md/en/mutex-trylock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/mutex-trylock.md -------------------------------------------------------------------------------- /md/en/mutex-unlock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/mutex-unlock.md -------------------------------------------------------------------------------- /md/en/mutex.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/mutex.md -------------------------------------------------------------------------------- /md/en/runtime-goid.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/runtime-goid.md -------------------------------------------------------------------------------- /md/en/runtime-gosched.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/runtime-gosched.md -------------------------------------------------------------------------------- /md/en/runtime-numGoroutine.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/runtime-numGoroutine.md -------------------------------------------------------------------------------- /md/en/runtime.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/runtime.md -------------------------------------------------------------------------------- /md/en/scheduler-join.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/scheduler-join.md -------------------------------------------------------------------------------- /md/en/scheduler-loop.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/scheduler-loop.md -------------------------------------------------------------------------------- /md/en/scheduler-run.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/scheduler-run.md -------------------------------------------------------------------------------- /md/en/scheduler.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/scheduler.md -------------------------------------------------------------------------------- /md/en/select.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/select.md -------------------------------------------------------------------------------- /md/en/selector-construct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/selector-construct.md -------------------------------------------------------------------------------- /md/en/selector-loop.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/selector-loop.md -------------------------------------------------------------------------------- /md/en/selector-select.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/selector-select.md -------------------------------------------------------------------------------- /md/en/selector.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/selector.md -------------------------------------------------------------------------------- /md/en/time-after.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/time-after.md -------------------------------------------------------------------------------- /md/en/time-sleep.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/time-sleep.md -------------------------------------------------------------------------------- /md/en/time-tick.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/time-tick.md -------------------------------------------------------------------------------- /md/en/time.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/time.md -------------------------------------------------------------------------------- /md/en/waitgroup-add.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/waitgroup-add.md -------------------------------------------------------------------------------- /md/en/waitgroup-construct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/waitgroup-construct.md -------------------------------------------------------------------------------- /md/en/waitgroup-destruct.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/waitgroup-destruct.md -------------------------------------------------------------------------------- /md/en/waitgroup-done.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/waitgroup-done.md -------------------------------------------------------------------------------- /md/en/waitgroup-wait.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/waitgroup-wait.md -------------------------------------------------------------------------------- /md/en/waitgroup.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/md/en/waitgroup.md -------------------------------------------------------------------------------- /src/basic_functions.cc: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2014 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Authors: Andi Gutmans | 16 | | Zeev Suraski | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #include "stdinc.h" 23 | #include "php_main.h" 24 | #include "ext/standard/basic_functions.h" 25 | 26 | #if PHP_MAJOR_VERSION < 7 27 | 28 | static int user_shutdown_function_call(php_shutdown_function_entry *shutdown_function_entry TSRMLS_DC) /* {{{ */ 29 | { 30 | zval retval; 31 | char *function_name; 32 | 33 | if (!zend_is_callable(shutdown_function_entry->arguments[0], 0, &function_name TSRMLS_CC)) { 34 | php_error(E_WARNING, "(Registered shutdown functions) Unable to call %s() - function does not exist", function_name); 35 | if (function_name) { 36 | efree(function_name); 37 | } 38 | return 0; 39 | } 40 | if (function_name) { 41 | efree(function_name); 42 | } 43 | 44 | if (call_user_function(EG(function_table), NULL, 45 | shutdown_function_entry->arguments[0], 46 | &retval, 47 | shutdown_function_entry->arg_count - 1, 48 | shutdown_function_entry->arguments + 1 49 | TSRMLS_CC ) == SUCCESS) 50 | { 51 | zval_dtor(&retval); 52 | } 53 | return 0; 54 | } 55 | /* }}} */ 56 | 57 | void php_free_shutdown_functions(TSRMLS_D) /* {{{ */ 58 | { 59 | if (BG(user_shutdown_function_names)) 60 | zend_try { 61 | zend_hash_destroy(BG(user_shutdown_function_names)); 62 | FREE_HASHTABLE(BG(user_shutdown_function_names)); 63 | BG(user_shutdown_function_names) = NULL; 64 | } zend_catch { 65 | /* maybe shutdown method call exit, we just ignore it */ 66 | FREE_HASHTABLE(BG(user_shutdown_function_names)); 67 | BG(user_shutdown_function_names) = NULL; 68 | } zend_end_try(); 69 | } 70 | /* }}} */ 71 | 72 | void php_call_shutdown_functions(TSRMLS_D) /* {{{ */ 73 | { 74 | if (BG(user_shutdown_function_names)) { 75 | zend_try { 76 | zend_hash_apply(BG(user_shutdown_function_names), (apply_func_t) user_shutdown_function_call TSRMLS_CC); 77 | } 78 | zend_end_try(); 79 | php_free_shutdown_functions(TSRMLS_C); 80 | } 81 | } 82 | /* }}} */ 83 | 84 | #endif 85 | 86 | /* 87 | * Local variables: 88 | * tab-width: 4 89 | * c-basic-offset: 4 90 | * End: 91 | * vim600: fdm=marker 92 | * vim: noet sw=4 ts=4 93 | */ 94 | -------------------------------------------------------------------------------- /src/co_recursive_mutex.h: -------------------------------------------------------------------------------- 1 | #ifndef __MUTEX_H__ 2 | #define __MUTEX_H__ 3 | 4 | #include 5 | #include 6 | 7 | namespace co{ 8 | 9 | class CoRecursiveMutex 10 | { 11 | CoMutex mutex_; 12 | uint64_t task_id_; 13 | uint64_t lock_count_; 14 | 15 | public: 16 | CoRecursiveMutex(bool signaled = true) : task_id_(~0), lock_count_(0){ 17 | if(!signaled){ 18 | mutex_.lock(); 19 | lock_count_ = 1; 20 | task_id_ = g_Scheduler.GetCurrentTaskID(); 21 | } 22 | } 23 | 24 | ~CoRecursiveMutex() {} 25 | 26 | void lock(){ 27 | auto my_task_id = g_Scheduler.GetCurrentTaskID(); 28 | if(lock_count_ > 0 && task_id_ == my_task_id){ 29 | // locked by myself 30 | ++lock_count_; 31 | return; 32 | } 33 | 34 | //1. not locked, or 2. locked by other task 35 | mutex_.lock(); 36 | ++ lock_count_; 37 | task_id_ = my_task_id; 38 | return; 39 | } 40 | 41 | bool try_lock(){ 42 | auto my_task_id = g_Scheduler.GetCurrentTaskID(); 43 | if(lock_count_ > 0 && task_id_ == my_task_id){ 44 | // locked by myself 45 | ++lock_count_; 46 | return true; 47 | } 48 | 49 | //1. not locked, or 2. locked by other task 50 | if( !mutex_.try_lock() ) return false; 51 | ++ lock_count_; 52 | task_id_ = my_task_id; 53 | return true; 54 | } 55 | 56 | bool is_lock(){ 57 | return mutex_.is_lock(); 58 | } 59 | 60 | void unlock(){ 61 | auto my_task_id = g_Scheduler.GetCurrentTaskID(); 62 | /* 63 | can free mutex not owned by myself 64 | in theory there could be 2 threads releasing the lock at the same 65 | time, thus need an extra lock here to ensure the code before <-- is atomic 66 | however releasing lock owned by other thread is not an often case, 67 | we chose to ignore this case for optimal performance... 68 | */ 69 | if(lock_count_ > 0 /*&& task_id_ == my_task_id*/) 70 | { 71 | --lock_count_; 72 | if(lock_count_ == 0){ 73 | task_id_ = ~0; 74 | mutex_.unlock(); 75 | } 76 | } 77 | /*<--*/ 78 | } 79 | }; 80 | 81 | } 82 | #endif -------------------------------------------------------------------------------- /src/co_wait_group.h: -------------------------------------------------------------------------------- 1 | #ifndef __WAIT_GROUP_H__ 2 | #define __WAIT_GROUP_H__ 3 | 4 | #include 5 | #include 6 | 7 | namespace co{ 8 | 9 | class CoWaitGroup 10 | { 11 | CoMutex mutex_; 12 | int64_t count_; 13 | 14 | public: 15 | CoWaitGroup() : count_(0) {} 16 | ~CoWaitGroup() {} 17 | 18 | int64_t Add(int64_t delta){ 19 | mutex_.lock(); 20 | count_ += delta; 21 | mutex_.unlock(); 22 | 23 | return count_; 24 | } 25 | 26 | int64_t Done(){ 27 | return Add(-1); 28 | } 29 | 30 | int64_t Count(){ 31 | return count_; 32 | } 33 | 34 | void Wait(){ 35 | 36 | // if not in any go routine 37 | // run the scheduer until the count reaches 0 38 | // otherwise yield until the count reaches 0 39 | 40 | if( !g_Scheduler.GetCurrentTaskID() ){ 41 | while(count_ > 0){ 42 | g_Scheduler.Run(); 43 | } 44 | }else{ 45 | while( count_ > 0 ){ 46 | g_Scheduler.CoYield(); 47 | } 48 | } 49 | } 50 | }; 51 | 52 | } 53 | #endif -------------------------------------------------------------------------------- /src/defer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copied from https://github.com/sismvg/___defer/tree/master/just_playing 3 | */ 4 | #ifndef __DEFER_H__ 5 | #define __DEFER_H__ 6 | 7 | #include 8 | 9 | /* 10 | * internal defer implementation, not expected to be used directly 11 | */ 12 | namespace _defer 13 | { 14 | class __defer 15 | { 16 | 17 | public: 18 | __defer(){} 19 | ~__defer(){ _fn(); } 20 | 21 | __defer(const __defer&) = delete; 22 | __defer* operator &() = delete; 23 | const __defer* operator &() const = delete; 24 | __defer& operator=(const __defer&) = delete; 25 | 26 | template 27 | inline void operator << (Func fn){ 28 | _fn = fn; 29 | } 30 | 31 | template 32 | inline void operator()(Func fn, Arg... arg){ 33 | _fn=std::bind(fn, arg...); 34 | } 35 | 36 | private: 37 | std::function _fn; 38 | static void* operator new(std::size_t); 39 | static void operator delete(void*); 40 | }; 41 | } 42 | 43 | #define _DEFER_MAKE_NAME_IMPL(line) __defer_##line 44 | #define _DEFER_MAKE_NAME(line) _DEFER_MAKE_NAME_IMPL(line) 45 | #define _DEFER_RANDOM_OBJECT(class, line) class _DEFER_MAKE_NAME(line) 46 | 47 | /* 48 | * application should use defer / defer_call instead of using __defer directly 49 | */ 50 | #define defer \ 51 | _DEFER_RANDOM_OBJECT(::_defer::__defer, __LINE__); \ 52 | _DEFER_MAKE_NAME(__LINE__) << [&](void) 53 | #define defer_call \ 54 | _DEFER_RANDOM_OBJECT(::_defer::__defer, __LINE__);\ 55 | _DEFER_MAKE_NAME(__LINE__) 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/freeable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace co 4 | { 5 | 6 | #define __interface__ struct 7 | 8 | __interface__ Freeable{ 9 | virtual void Free() = 0; 10 | }; 11 | 12 | class FreeableImpl : public Freeable{ 13 | public: 14 | ALWAYS_INLINE void Free(){ 15 | delete this; 16 | } 17 | 18 | virtual ~FreeableImpl(){ 19 | } 20 | }; 21 | 22 | } -------------------------------------------------------------------------------- /src/global_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef ZTS 4 | #define THREAD_LOCAL thread_local 5 | #else 6 | #define THREAD_LOCAL 7 | #endif 8 | 9 | #define PHPGO_MAX_GO_ARGS (256 + 2) // goo($option, $callable, $arg1,...,arg256); -------------------------------------------------------------------------------- /src/go.h: -------------------------------------------------------------------------------- 1 | #ifndef __GO_H__ 2 | #define __GO_H__ 3 | 4 | enum GoRoutineOptions{ 5 | // change of http globals in children go routines does not 6 | // affect those of parent go routine 7 | gro_default = 0x0, 8 | gro_isolate_http_globals = 0x1 9 | }; 10 | 11 | /*pure functions*/ 12 | bool phpgo_initialize(); 13 | void phpgo_go_debug(unsigned long debug_flag); 14 | bool phpgo_go(uint64_t go_routine_options, uint32_t stack_size, zend_uint argc, PHPGO_ARG_TYPE *args TSRMLS_DC); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/go_chan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "zend.h" 6 | #include "TSRM.h" 7 | #include "zend_API.h" 8 | #include "zend_variables_persist.h" 9 | 10 | #define MAX_CHAN_NAME_LEN (32) 11 | 12 | typedef struct{ 13 | size_t ref_count; 14 | char name[ MAX_CHAN_NAME_LEN + sizeof(uint32_t)]; // 4byte padding multi-byte string ending 15 | size_t name_len; 16 | bool closed; 17 | bool copy; //whether or not we copy the zval data from thread local 18 | //into shared memory on channel push; copy is required 19 | //when sending data to a different thread 20 | void* chan; 21 | } ChannelInfo; 22 | 23 | 24 | struct ChannelData{ 25 | #ifdef ZTS 26 | THREAD_T from_thread_id; 27 | #endif 28 | uint64_t from_task_id; 29 | bool copy; 30 | zval* z; 31 | 32 | ChannelData(zval* zv, bool copy_flag TSRMLS_DC); 33 | ~ChannelData(){ 34 | if(!z) return; 35 | if(copy){ 36 | zval_persistent_ptr_dtor(&z); 37 | //php7: the dtor won't free the z for us 38 | //for a permenent z, we've finished using it: it had been copied to 39 | //thread local heap in GoChan::Pop()/TryPop() 40 | //free it here 41 | //note: PHPGO_FREE_PERMENENT_PZVAL() has no effect in php5 42 | PHPGO_FREE_PERMENENT_PZVAL(z); 43 | }else{ 44 | phpgo_zval_ptr_dtor(&z); 45 | //php7: the dtor won't free the z for us 46 | //note: PHPGO_FREE_PZVAL() has no effect in php5 47 | PHPGO_FREE_PZVAL(z); 48 | } 49 | } 50 | }; 51 | 52 | class GoChan{ 53 | static std::map map_name_to_chinfo; 54 | static std::atomic_flag chan_lock; 55 | public: 56 | enum RCode{ 57 | success = 0, 58 | channel_closed = 1, 59 | channel_not_ready = 2 60 | }; 61 | 62 | public: 63 | static void* Create(unsigned long capacity, char* name, size_t name_len, bool copy); 64 | static void Destroy(void* handle); 65 | static bool Close(void* handle); 66 | static RCode Push(void* handle, zval* z TSRMLS_DC); 67 | static zval* Pop(void* handle); 68 | static RCode TryPush(void* handle, zval* z TSRMLS_DC); 69 | static zval* TryPop(void* handle); 70 | 71 | /*convert chan zval to the channel ChannelInfo*/ 72 | static ChannelInfo* ZvalToChannelInfo(zval* z_chan TSRMLS_DC); 73 | }; -------------------------------------------------------------------------------- /src/go_mutex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "co_recursive_mutex.h" 3 | 4 | /* 5 | * mutex 6 | */ 7 | class GoMutex{ 8 | public: 9 | static void* Create(bool signaled){ 10 | return new ::co::CoRecursiveMutex(signaled); 11 | } 12 | 13 | static void Lock(void* mutex){ 14 | auto mutex_obj = (::co::CoRecursiveMutex*)mutex; 15 | 16 | // if it's not in any go routine, 17 | // run the scheduler (so that the go routines will run and may 18 | // release lock) until the lock is obtained 19 | if( !co_sched.IsCoroutine() ){ 20 | while( !mutex_obj->try_lock() ) { 21 | co_sched.Run(); 22 | } 23 | return; 24 | } 25 | 26 | // in a go routine, it's safe to just lock 27 | mutex_obj->lock(); 28 | } 29 | 30 | static void Unlock(void* mutex){ 31 | auto mutex_obj = (::co::CoRecursiveMutex*)mutex; 32 | mutex_obj->unlock(); 33 | } 34 | 35 | static bool TryLock(void* mutex){ 36 | auto mutex_obj = (::co::CoRecursiveMutex*)mutex; 37 | return mutex_obj->try_lock(); 38 | } 39 | 40 | static bool IsLock(void* mutex){ 41 | auto mutex_obj = (::co::CoRecursiveMutex*)mutex; 42 | return mutex_obj->is_lock(); 43 | } 44 | 45 | static void Destroy(void* mutex){ 46 | auto mutex_obj = (::co::CoRecursiveMutex*)mutex; 47 | delete mutex_obj; 48 | } 49 | }; -------------------------------------------------------------------------------- /src/go_runtime.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * the go runtime library 3 | */ 4 | #include "go_runtime.h" 5 | 6 | uint64_t phpgo_go_runtime_goid(){ 7 | return co_sched.GetCurrentTaskID(); 8 | } 9 | 10 | uint64_t phpgo_go_runtime_num_goroutine(){ 11 | return co_sched.TaskCount(); 12 | } 13 | 14 | void phpgo_go_runtime_gosched(){ 15 | if(co_sched.IsCoroutine()){ 16 | co_sched.CoYield(); 17 | }else{ 18 | co_sched.Run(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/go_runtime.h: -------------------------------------------------------------------------------- 1 | /* 2 | * the go runtime library 3 | */ 4 | #pragma once 5 | #include 6 | 7 | #ifndef co_sched 8 | #define co_sched g_Scheduler 9 | #endif 10 | 11 | uint64_t phpgo_go_runtime_num_goroutine(); 12 | void phpgo_go_runtime_gosched(); 13 | uint64_t phpgo_go_runtime_goid(); 14 | 15 | -------------------------------------------------------------------------------- /src/go_scheduler.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/birdwyx/phpgo/2081ce6c4ce37297e02eda8822df3df0b6d5dd9c/src/go_scheduler.cc -------------------------------------------------------------------------------- /src/go_scheduler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #ifndef co_sched 5 | #define co_sched g_Scheduler 6 | #endif 7 | 8 | class GoScheduler{ 9 | public: 10 | static uint32_t Run(){ 11 | if (co_sched.IsCoroutine()) { 12 | zend_error(E_ERROR, "phpgo: error: run must be called outside a go routine\n"); 13 | return 0; 14 | } 15 | return co_sched.Run(); 16 | } 17 | 18 | /* 20180429: not used any more 19 | static void Join(uint32_t tasks_left){ 20 | if (co_sched.IsCoroutine()) { 21 | zend_error(E_ERROR, "phpgo: error: join must be called outside a go routine\n"); 22 | return; 23 | } 24 | co_sched.RunUntilNoTask(tasks_left); 25 | }*/ 26 | 27 | static void Loop(){ 28 | if (co_sched.IsCoroutine()) { 29 | zend_error(E_ERROR, "phpgo: error: loop must be called outside a go routine\n"); 30 | return; 31 | } 32 | co_sched.RunLoop(); 33 | } 34 | }; -------------------------------------------------------------------------------- /src/go_select.cc: -------------------------------------------------------------------------------- 1 | #include "stdinc.h" 2 | #include "go_select.h" 3 | #include "go_chan.h" 4 | #include 5 | /* 6 | * select - case 7 | */ 8 | bool phpgo_select(GO_SELECT_CASE* case_array, long case_count TSRMLS_DC){ 9 | if(!case_count) return false; 10 | 11 | GO_SELECT_CASE* selected_case = nullptr; 12 | while (!selected_case) { 13 | //calling srand everytime will cuase the result less randomized,remove 14 | //srand(time(NULL)); 15 | auto r = rand(); 16 | auto start = r % case_count; 17 | 18 | bool any_case_ready = false; 19 | 20 | zval* z_chan = nullptr; 21 | zval* z_handler = nullptr; 22 | co::Channel* chanptr = nullptr; 23 | ChannelInfo* chinfo = nullptr; 24 | 25 | auto i = start; 26 | do{ 27 | switch(case_array[i].case_type){ 28 | case GO_CASE_TYPE_CASE: 29 | z_chan = case_array[i].chan; 30 | chinfo = GoChan::ZvalToChannelInfo(z_chan TSRMLS_CC); 31 | if( !chinfo ){ 32 | zend_error(E_ERROR, "phpgo: phpgo_select: null channel"); 33 | return false; 34 | } 35 | if(case_array[i].op == GO_CASE_OP_READ){ 36 | // GoChan::TryPop: 37 | // if data not ready to read, return nullptr 38 | // if channel is closed, return ZVAL_NULL 39 | // otherwise return the read zval 40 | zval* data = GoChan::TryPop(chinfo); 41 | if(data) { 42 | PHPGO_REPLACE_ZVAL_VALUE(&case_array[i].value, data, 1 /*invoke zval_copy_ctor*/); 43 | phpgo_zval_ptr_dtor(&data); 44 | PHPGO_FREE_PZVAL(data); //efree(data) on php7 and no effect on php5 45 | selected_case = &case_array[i]; 46 | goto exit_do_while; 47 | } 48 | }else if(case_array[i].op == GO_CASE_OP_WRITE){ 49 | auto rc = GoChan::TryPush(chinfo, case_array[i].value TSRMLS_CC); 50 | if( rc==GoChan::RCode::success ){ 51 | //todo: php7 need to test the write of non-scalar value (array/string etc) 52 | //phpgo_zval_add_ref(&case_array[i].value); 53 | selected_case = &case_array[i]; 54 | goto exit_do_while; 55 | } 56 | }else{ 57 | //error 58 | } 59 | break; 60 | case GO_CASE_TYPE_DEFAULT: 61 | selected_case = &case_array[i]; 62 | break; 63 | default: 64 | break; 65 | } // switch(case_array[i].case_type) 66 | 67 | i = (i+1) % case_count; 68 | }while (i != start ); 69 | 70 | exit_do_while: 71 | PHP5_AND_BELOW( 72 | zval* return_value = nullptr; 73 | defer{ if(return_value) zval_ptr_dtor(&return_value); }; 74 | ); 75 | PHP7_AND_ABOVE( zval return_value; ); 76 | 77 | if( selected_case ){ 78 | PHPGO_ARG_TYPE* args = nullptr; 79 | auto argc = 0; 80 | if(selected_case->case_type != GO_CASE_TYPE_DEFAULT){ 81 | args = (PHPGO_ARG_TYPE*)safe_emalloc(1, sizeof(PHPGO_ARG_TYPE), 0); 82 | args[0] = PHP5_VS_7(&selected_case->value, *selected_case->value); 83 | argc = 1; 84 | } 85 | 86 | phpgo_zval_add_ref(&selected_case->callback); 87 | if( call_user_function_ex( 88 | EG(function_table), 89 | NULL, 90 | selected_case->callback, // the callback callable 91 | &return_value, // zval**(PHP5)/zval*(PHP7) to receive return value 92 | argc, // the parameter number required by the callback 93 | args, // the parameter list of the callback 94 | 1, 95 | NULL TSRMLS_CC 96 | ) != SUCCESS) { 97 | zend_error(E_ERROR, "phpgo: execution of go routine faild"); 98 | //goto cleanup; 99 | } 100 | 101 | phpgo_zval_ptr_dtor(&selected_case->callback); 102 | if(args) efree(args); 103 | }else{ 104 | if( co_sched.IsCoroutine() ){ 105 | co_sched.CoYield(); 106 | }else{ 107 | co_sched.Run(); 108 | } 109 | } 110 | } 111 | 112 | return true; 113 | } 114 | -------------------------------------------------------------------------------- /src/go_select.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "zend.h" 3 | 4 | #define GO_CASE_OP_READ 1 5 | #define GO_CASE_OP_WRITE 2 6 | 7 | #define GO_CASE_TYPE_CASE 1 8 | #define GO_CASE_TYPE_DEFAULT 2 9 | 10 | typedef struct{ 11 | long case_type; 12 | zval* chan; 13 | long op; 14 | zval* value; 15 | zval* callback; 16 | } GO_SELECT_CASE; 17 | 18 | typedef struct{ 19 | long case_count; 20 | GO_SELECT_CASE* case_array; 21 | } GO_SELECTOR; 22 | 23 | 24 | bool phpgo_select(GO_SELECT_CASE* case_array, long case_count TSRMLS_DC); -------------------------------------------------------------------------------- /src/go_time.cc: -------------------------------------------------------------------------------- 1 | #include "stdinc.h" 2 | #include "php_phpgo.h" 3 | #include "go_time.h" 4 | #include "zend_API.h" 5 | #include "go_chan.h" 6 | #include 7 | 8 | THREAD_LOCAL TimerSet GoTime::tls_timer_set; 9 | 10 | bool GoTime::CreateTimer( 11 | const char* chan_name, 12 | uint64_t nano_seconds, 13 | bool is_periodic, 14 | bool& go_creation 15 | ){ 16 | static THREAD_LOCAL co_chan* td_chan = nullptr; 17 | 18 | go_creation = false; 19 | if( !td_chan ){ 20 | td_chan = new co_chan(MAX_TIMER_CHAN_CAPACITY); 21 | if( !td_chan ) { 22 | zend_error(E_ERROR, "faile to creat channel for go timer processor"); 23 | return false; 24 | } 25 | 26 | CreateGoRoutine(td_chan); 27 | go_creation = true; 28 | } 29 | 30 | // push the timer data to chan 31 | auto td = new TimerData(chan_name, nano_seconds, is_periodic); 32 | *td_chan << td; 33 | 34 | return true; 35 | } 36 | 37 | void GoTime::CreateGoRoutine( co_chan* td_chan ){ 38 | go [=](){ 39 | while(true){ 40 | TimerData* td = nullptr; 41 | 42 | /*proccess expired timer*/ 43 | while( td = PopExpired() ){ 44 | zval* z = nullptr; 45 | void* timer_chan = nullptr; 46 | defer{ 47 | delete td; 48 | if(z) phpgo_zval_ptr_dtor(&z); 49 | if(timer_chan) { 50 | // finish using the timer_chan, del-ref it 51 | // the timer_chan was just add-ref'ed by GoChan::Create() below 52 | GoChan::Destroy(timer_chan); 53 | } 54 | }; // will defer work in while?? will need to confirm 55 | 56 | timer_chan = GoChan::Create(1, td->chan_name, strlen(td->chan_name), true /*copy=true*/); 57 | if(!timer_chan){ 58 | zend_error(E_WARNING, "faile to get channel for timer %s", td->chan_name); 59 | continue; 60 | } 61 | //GoChan::Close(timer_chan); // close chan to inform timer creator 62 | 63 | //return the current time,in the same format as microtime() 64 | struct timeval tp = {0}; 65 | gettimeofday(&tp, NULL); 66 | double d = (double)tp.tv_sec + (double)tp.tv_usec/1000000.00; 67 | 68 | PHPGO_MAKE_STD_ZVAL(z); 69 | ZVAL_DOUBLE(z,d); 70 | 71 | TSRMLS_FETCH(); 72 | GoChan::RCode rc = GoChan::TryPush(timer_chan, z TSRMLS_CC); 73 | if( GoChan::RCode::success != rc ){ 74 | zend_error(E_WARNING, 75 | "faile to activate timer %s on expiry: push to timer channel failed with error code %d", 76 | td->chan_name, rc); 77 | continue; 78 | } 79 | } 80 | 81 | /*check if any new timer to create*/ 82 | while( td_chan->TryPop(td) ){ 83 | if(td) PlaceTimer(td); 84 | } 85 | 86 | usleep( 10*1000 ); // sleep for a 10ms 87 | } 88 | }; 89 | } 90 | 91 | void GoTime::PlaceTimer(TimerData* td){ 92 | td->expire_tick = Clock() + td->delta;; 93 | tls_timer_set.insert(td); 94 | } 95 | 96 | TimerData* GoTime::PopExpired(){ 97 | auto it = tls_timer_set.begin(); 98 | if( it == tls_timer_set.end() ) 99 | return nullptr; 100 | 101 | auto td = *it; 102 | if( td->expire_tick > Clock() ) return nullptr; 103 | 104 | tls_timer_set.erase(it); 105 | if( td->is_periodic ){ 106 | auto td_next = new TimerData(*td); 107 | PlaceTimer(td_next); 108 | } 109 | return td; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /src/go_time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "global_defs.h" 3 | #include "defer.h" 4 | #include "zend.h" 5 | #include "TSRM.h" 6 | #include "go_chan.h" 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | #define MAX_TIMER_CHAN_CAPACITY (1000) 13 | 14 | struct TimerData{ 15 | char chan_name[MAX_CHAN_NAME_LEN + sizeof(uint32_t)]; 16 | uint64_t delta; // in nanoseconds 17 | bool is_periodic; 18 | uint64_t expire_tick; // the tick @ which this timer should expire 19 | 20 | public: 21 | TimerData(const char* chan_name, uint64_t delta, bool is_periodic){ 22 | strncpy(this->chan_name, chan_name, MAX_CHAN_NAME_LEN); 23 | *(uint32_t*)&(this->chan_name[MAX_CHAN_NAME_LEN]) = 0; //padding 4 zeros 24 | this->is_periodic = is_periodic; 25 | this->delta = delta; 26 | this->expire_tick = 0; 27 | } 28 | 29 | TimerData(TimerData& td){ 30 | *this = td; 31 | } 32 | }; 33 | 34 | class TickLess{ 35 | public: 36 | bool operator()(const TimerData* td1, const TimerData* td2){ 37 | return td1->expire_tick < td2->expire_tick; 38 | } 39 | }; 40 | 41 | typedef multiset TimerSet; 42 | 43 | class GoTime{ 44 | private: 45 | static THREAD_LOCAL TimerSet tls_timer_set; 46 | private: 47 | static void CreateGoRoutine( co_chan* td_chan ); 48 | static void PlaceTimer( TimerData* td ); 49 | static TimerData* PopExpired(); 50 | static uint64_t Clock(){ 51 | struct timespec tv; 52 | clock_gettime(CLOCK_MONOTONIC, &tv); 53 | return tv.tv_sec * GoTime::Second + tv.tv_nsec; 54 | } 55 | 56 | public: 57 | static const uint64_t Nanosecond = 1; 58 | static const uint64_t Microsecond = 1000 * Nanosecond; 59 | static const uint64_t Millisecond = 1000 * Microsecond; 60 | static const uint64_t Second = 1000 * Millisecond; 61 | static const uint64_t Minute = 60 * Second; 62 | static const uint64_t Hour = 60 * Minute; 63 | 64 | public: 65 | static bool CreateTimer(const char* chan_name, uint64_t micro_seconds, 66 | bool is_periodic, bool& go_creation); 67 | static void Sleep(uint64_t nanoseconds){ 68 | if(co_sched.IsCoroutine()){ 69 | co_sleep(nanoseconds/Millisecond); 70 | }else{ 71 | usleep(nanoseconds/Microsecond); 72 | } 73 | } 74 | 75 | }; -------------------------------------------------------------------------------- /src/go_wait_group.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "co_wait_group.h" 3 | 4 | /* 5 | * waitgroup 6 | */ 7 | class GoWaitGroup{ 8 | public: 9 | static void* Create(){ 10 | return new ::co::CoWaitGroup(); 11 | } 12 | 13 | static int64_t Add(void* wg, int64_t delta){ 14 | auto wg_obj = (::co::CoWaitGroup*)wg; 15 | return wg_obj->Add(delta); 16 | } 17 | 18 | static int64_t Done(void* wg){ 19 | auto wg_obj = (::co::CoWaitGroup*)wg; 20 | return wg_obj->Done(); 21 | } 22 | 23 | static int64_t Count(void* wg){ 24 | auto wg_obj = (::co::CoWaitGroup*)wg; 25 | return wg_obj->Count(); 26 | } 27 | 28 | static void Wait(void* wg){ 29 | auto wg_obj = (::co::CoWaitGroup*)wg; 30 | wg_obj->Wait(); 31 | } 32 | 33 | static bool Destruct(void* wg){ 34 | auto wg_obj = (::co::CoWaitGroup*)wg; 35 | delete wg_obj; 36 | } 37 | }; -------------------------------------------------------------------------------- /src/php_phpgo.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2015 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_PHPGO_H 22 | #define PHP_PHPGO_H 23 | 24 | extern zend_module_entry phpgo_module_entry; 25 | #define phpext_phpgo_ptr &phpgo_module_entry 26 | 27 | #define PHP_PHPGO_VERSION "0.1.0" /* Replace with version number for your extension */ 28 | 29 | #ifdef PHP_WIN32 30 | # define PHP_PHPGO_API __declspec(dllexport) 31 | #elif defined(__GNUC__) && __GNUC__ >= 4 32 | # define PHP_PHPGO_API __attribute__ ((visibility("default"))) 33 | #else 34 | # define PHP_PHPGO_API 35 | #endif 36 | 37 | #ifdef ZTS 38 | #include "TSRM.h" 39 | #endif 40 | 41 | #define PHPGO_NS "go" 42 | #define PHPGO_NS_NAME(name) PHPGO_NS"\\"#name 43 | #define PHPGO_NS_FE(ns, name, arg_info) ZEND_NS_NAMED_FE(#ns, name, ZEND_FN(ns##_##name), arg_info) 44 | 45 | PHP_MINIT_FUNCTION(phpgo); 46 | PHP_MSHUTDOWN_FUNCTION(phpgo); 47 | PHP_RINIT_FUNCTION(phpgo); 48 | PHP_RSHUTDOWN_FUNCTION(phpgo); 49 | PHP_MINFO_FUNCTION(phpgo); 50 | 51 | PHP_FUNCTION(go); 52 | PHP_FUNCTION(goo); 53 | PHP_FUNCTION(go_debug); 54 | 55 | PHP_FUNCTION(select); 56 | 57 | PHP_METHOD(Scheduler, Run); 58 | PHP_METHOD(Scheduler, Join); 59 | PHP_METHOD(Scheduler, Loop); 60 | 61 | PHP_METHOD(Runtime, NumGoroutine); 62 | PHP_METHOD(Runtime, Gosched); 63 | PHP_METHOD(Runtime, Goid); 64 | PHP_METHOD(Runtime, Quit); 65 | 66 | PHP_METHOD(Chan, __construct); 67 | PHP_METHOD(Chan, Push); 68 | PHP_METHOD(Chan, Pop); 69 | PHP_METHOD(Chan, TryPush); 70 | PHP_METHOD(Chan, TryPop); 71 | PHP_METHOD(Chan, Close); 72 | PHP_METHOD(Chan, __destruct); 73 | 74 | PHP_METHOD(Mutex, __construct); 75 | PHP_METHOD(Mutex, Lock); 76 | PHP_METHOD(Mutex, Unlock); 77 | PHP_METHOD(Mutex, TryLock); 78 | PHP_METHOD(Mutex, IsLock); 79 | PHP_METHOD(Mutex, __destruct); 80 | 81 | PHP_METHOD(WaitGroup, __construct); 82 | PHP_METHOD(WaitGroup, Add); 83 | PHP_METHOD(WaitGroup, Done); 84 | PHP_METHOD(WaitGroup, Wait); 85 | PHP_METHOD(WaitGroup, __destruct); 86 | 87 | PHP_METHOD(Selector, __construct); 88 | PHP_METHOD(Selector, Select); 89 | PHP_METHOD(Selector, Loop); 90 | PHP_METHOD(Selector, __destruct); 91 | 92 | PHP_METHOD(Time, Tick); 93 | PHP_METHOD(Time, After); 94 | PHP_METHOD(Time, Sleep); 95 | 96 | /* 97 | Declare any global variables you may need between the BEGIN 98 | and END macros here: 99 | */ 100 | 101 | ZEND_BEGIN_MODULE_GLOBALS(phpgo) 102 | bool phpgo_initialized = false; 103 | uint32_t running_internal_go_routines = 0; 104 | ZEND_END_MODULE_GLOBALS(phpgo) 105 | 106 | 107 | /* In every utility function you add that needs to use variables 108 | in php_phpgo_globals, call TSRMLS_FETCH(); after declaring other 109 | variables used by that function, or better yet, pass in TSRMLS_CC 110 | after the last function argument and declare your utility function 111 | with TSRMLS_DC after the last declared argument. Always refer to 112 | the globals in your function as PHPGO_G(variable). You are 113 | encouraged to rename these macros something shorter, see 114 | examples in any other php module directory. 115 | */ 116 | 117 | #ifdef ZTS 118 | #define PHPGO_G(v) TSRMG(phpgo_globals_id, zend_phpgo_globals *, v) 119 | #else 120 | #define PHPGO_G(v) (phpgo_globals.v) 121 | #endif 122 | 123 | 124 | /*debug functions*/ 125 | void phpgo_hex_dump(void* buff, size_t n); 126 | void phpgo_zval_dump(zval* zv); 127 | void phpgo_var_dump(zval* zv); 128 | 129 | #endif /* PHP_PHPGO_H */ 130 | 131 | 132 | /* 133 | * Local variables: 134 | * tab-width: 4 135 | * c-basic-offset: 4 136 | * End: 137 | * vim600: noet sw=4 ts=4 fdm=marker 138 | * vim<600: noet sw=4 ts=4 139 | */ 140 | -------------------------------------------------------------------------------- /src/phpgo_context.cc: -------------------------------------------------------------------------------- 1 | #include "stdinc.h" 2 | #include "php_phpgo.h" 3 | #include "go.h" 4 | #include "phpgo_context.h" 5 | 6 | // the scheduler may be executed in multiple thread: 7 | // use thread local variable to store the scheduler EG's 8 | thread_local PhpgoSchedulerContext scheduler_ctx; -------------------------------------------------------------------------------- /src/stdinc.h: -------------------------------------------------------------------------------- 1 | #ifndef __STDINC_H__ 2 | #define __STDINC_H__ 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include "config.h" 6 | #endif 7 | 8 | #include "php.h" 9 | #include "php_ini.h" 10 | #include "ext/standard/info.h" 11 | 12 | #include "defer.h" 13 | #include "global_defs.h" 14 | #include "php_version_dependent.h" 15 | 16 | #endif -------------------------------------------------------------------------------- /src/task_listener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "php_phpgo.h" 3 | #include "phpgo_context.h" 4 | 5 | using namespace co; 6 | 7 | ZEND_EXTERN_MODULE_GLOBALS(phpgo) 8 | 9 | class PhpgoTaskListener : public Scheduler::TaskListener{ 10 | 11 | // the go routine sepecific EG's are stored in task local storage 12 | // thus each task has it's own copy 13 | kls_key_t phpgo_context_key; 14 | Scheduler::TaskListener* old_task_listener; 15 | 16 | public: 17 | PhpgoTaskListener(Scheduler::TaskListener* task_listener){ 18 | old_task_listener = task_listener; 19 | phpgo_context_key = TaskLocalStorage::CreateKey("PhpgoContext"); 20 | } 21 | 22 | /* 23 | virtual void onStart(uint64_t task_id) noexcept { 24 | printf("onStart(%ld)", task_id); 25 | }*/ 26 | 27 | /** 28 | * run by the scheduler each time when the task is about to swap in 29 | */ 30 | virtual void onSwapIn(uint64_t task_id) noexcept { 31 | /*printf("---------->onSwapIn(%ld)<-----------\n", task_id); 32 | defer{ 33 | printf("---------->onSwapIn(%ld) returns<-----------\n", task_id); 34 | };*/ 35 | 36 | if(old_task_listener){ 37 | old_task_listener->onSwapIn(task_id); 38 | } 39 | 40 | PhpgoSchedulerContext* sched_ctx = &scheduler_ctx; /*scheduler_ctx is thread local*/ 41 | PhpgoContext* ctx = (PhpgoContext*)TaskLocalStorage::GetSpecific(phpgo_context_key, task_id); 42 | 43 | // first time swap into a task: 44 | // return - not to do the PHPGO_SAVE/LOAD_CONTEXT since they will be done in the 45 | // phpgo_go() 46 | if(!ctx) return; 47 | 48 | // running -> sched_ctx and ctx -> running 49 | bool including_http_globals = ctx->go_routine_options & GoRoutineOptions::gro_isolate_http_globals; 50 | sched_ctx->SwapOut(including_http_globals); 51 | ctx->SwapIn(); 52 | } 53 | 54 | /** 55 | * run by the task each time when it's about to swap out 56 | */ 57 | virtual void onSwapOut(uint64_t task_id) noexcept { 58 | /*printf("---------->onSwapOut(%ld)<-----------\n", task_id); 59 | defer{ 60 | printf("---------->onSwapOut(%ld) returns<-----------\n", task_id); 61 | };*/ 62 | 63 | if(old_task_listener){ 64 | old_task_listener->onSwapOut(task_id); 65 | } 66 | 67 | PhpgoSchedulerContext* sched_ctx = &scheduler_ctx; 68 | PhpgoContext* ctx = (PhpgoContext*)TaskLocalStorage::GetSpecific(phpgo_context_key, task_id); 69 | if(!ctx) return; 70 | 71 | // running -> ctx and sched_ctx -> running 72 | ctx->SwapOut(); 73 | bool including_http_globals = ctx->go_routine_options & GoRoutineOptions::gro_isolate_http_globals; 74 | sched_ctx->SwapIn(including_http_globals); 75 | } 76 | 77 | /** 78 | * run by the task each time when it's going to finish running, either normally or abnormally 79 | */ 80 | virtual void onFinished(uint64_t task_id, const std::exception_ptr eptr) noexcept { 81 | /*printf("---------->onFinished(%ld)<-----------\n", task_id); 82 | defer{ 83 | printf("---------->onFinished(%ld) returns<-----------\n", task_id); 84 | };*/ 85 | 86 | PhpgoContext* ctx = (PhpgoContext*)TaskLocalStorage::GetSpecific(phpgo_context_key, task_id); 87 | if(!ctx) return; 88 | 89 | ctx->SetFinished(true); 90 | } 91 | }; 92 | 93 | -------------------------------------------------------------------------------- /src/task_local_storage.cc: -------------------------------------------------------------------------------- 1 | #include "task_local_storage.h" 2 | #include "freeable.h" 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace co { 9 | 10 | map TaskLocalStorage::kls_key_map; 11 | uint64_t TaskLocalStorage::kls_key_count = 0; 12 | vector> TaskLocalStorage::kls; 13 | std::mutex TaskLocalStorage::kls_mutex; 14 | 15 | kls_key_t TaskLocalStorage::CreateKey(string str_key){ 16 | kls_key_t key; 17 | if( (key = kls_key_map[str_key]) ){ 18 | return key; 19 | }else{ 20 | std::lock_guard guard(TaskLocalStorage::kls_mutex); 21 | kls_key_map[str_key] = ++kls_key_count; 22 | return kls_key_count; 23 | } 24 | } 25 | 26 | bool TaskLocalStorage::SetSpecific(kls_key_t key, Freeable* pointer){ 27 | Task* tk = g_Scheduler.GetCurrentTask(); 28 | 29 | if(!tk) return false; 30 | 31 | auto task_id = tk->id_; 32 | //note: task id starts from 1 33 | if(task_id >= MAX_TASK_CAPACITY) return false; 34 | 35 | //cout << "task:" << tk << " id:" << task_id << " size:" << kls.size() << " cap:" << kls.capacity() < guard(TaskLocalStorage::kls_mutex); 40 | auto cap = kls.capacity(); 41 | if( !cap ){ 42 | // first time 43 | kls.reserve( INITIAL_TASK_CAPACITY ); 44 | }else if( cap < task_id + 1 ){ 45 | // extend of capacity required 46 | if( task_id < MAX_TASK_CAPACITY ){ 47 | kls.reserve( 2 * task_id > MAX_TASK_CAPACITY ? MAX_TASK_CAPACITY : 2 * task_id ); 48 | }else{ 49 | //task id out of range 50 | return false; 51 | } 52 | }else{ 53 | // capacity ok, just fall-through 54 | 55 | } 56 | 57 | auto sz = kls.size(); cap = kls.capacity(); 58 | while( sz++ < cap ) { 59 | kls.push_back( vector() ); 60 | } 61 | // lock section 62 | } 63 | 64 | //cout << "task:" << tk << " id:" << tk->id_ << " size:" << kls.size() << " cap:" << kls.capacity() < MAX_KEY_CAPACITY ? MAX_KEY_CAPACITY : 2 * key ); 76 | }else{ 77 | //task id out of range 78 | return false; 79 | } 80 | }else{ 81 | // capacity ok, just fall-through 82 | 83 | } 84 | 85 | auto sz = task_kls.size(); cap = task_kls.capacity(); 86 | while( sz++ < cap ) { 87 | //cout << sz << endl; 88 | task_kls.push_back( nullptr ); 89 | } 90 | //cout << "keys capacity:" << task_kls.capacity() << " size:" << task_kls.size() << endl; 91 | //cout << "task:" << tk << " id:" << tk->id_ << " size:" << kls.size() << " cap:" << kls.capacity() <Free(); 96 | } 97 | 98 | task_kls[key] = pointer; // note: directly assignment wont change the kls[task_id][key].size (thus always 0) 99 | 100 | 101 | //cout << "keys capacity:" << task_kls.capacity() << " size:" << task_kls.size() << endl; 102 | //cout << "task:" << tk << " id:" << tk->id_ << " size:" << kls.size() << " cap:" << kls.capacity() <Free(); 121 | pointer = nullptr; 122 | } 123 | } 124 | } 125 | 126 | void TaskLocalStorage::Dump(){ 127 | 128 | for( auto& task_kls: kls ){ 129 | for( auto& pointer:task_kls){ 130 | cout << pointer << ","; 131 | } 132 | } 133 | cout << endl; 134 | 135 | } 136 | 137 | } //namespace co 138 | -------------------------------------------------------------------------------- /src/task_local_storage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define INITIAL_TASK_CAPACITY (1024 + 1) /* 0 not used */ 6 | #define MAX_TASK_CAPACITY (1024 * 1024 +1) /* 0 not used */ 7 | #define INITIAL_KEY_CAPACITY (16 + 1) /* 0 not used */ 8 | #define MAX_KEY_CAPACITY (1024 + 1) /* 0 not used */ 9 | 10 | namespace co 11 | { 12 | 13 | class Freeable; 14 | class Task; 15 | 16 | typedef uint64_t kls_key_t; 17 | 18 | class TaskLocalStorage{ 19 | static std::vector> kls; 20 | static std::map kls_key_map; 21 | static uint64_t kls_key_count; 22 | static std::mutex kls_mutex; 23 | 24 | public: 25 | static kls_key_t CreateKey(std::string str_key); 26 | static bool SetSpecific(kls_key_t key, Freeable* pointer); 27 | 28 | ALWAYS_INLINE static Freeable* GetSpecific(kls_key_t key){ 29 | //cout << "get key:" << key << endl; 30 | Task* tk = g_Scheduler.GetCurrentTask(); 31 | 32 | if(!tk) return nullptr; 33 | 34 | auto task_id = tk->id_; 35 | 36 | if(kls.size() < task_id +1) return nullptr; 37 | if(kls[task_id].size() < key + 1) return nullptr; 38 | 39 | return kls[task_id][key]; 40 | } 41 | 42 | ALWAYS_INLINE static Freeable* GetSpecific(kls_key_t key, uint64_t task_id){ 43 | //printf( "get key: %d task %d\n", key, task_id ); 44 | if(kls.size() < task_id +1) return nullptr; 45 | if(kls[task_id].size() < key + 1) return nullptr; 46 | 47 | return kls[task_id][key]; 48 | } 49 | 50 | static void FreeSpecifics(uint64_t); 51 | static void Dump(); 52 | }; 53 | 54 | template 55 | class TaskLocalMaker { 56 | T* tlv; 57 | kls_key_t key; 58 | 59 | public: 60 | TaskLocalMaker(std::string str_key){ 61 | tlv = nullptr; 62 | key = TaskLocalStorage::CreateKey(str_key); 63 | if( g_Scheduler.GetCurrentTask() && !(tlv = (T*)TaskLocalStorage::GetSpecific(key)) ){ 64 | tlv = new T(); 65 | TaskLocalStorage::SetSpecific(key, tlv); 66 | } 67 | } 68 | 69 | ALWAYS_INLINE operator T*(){ 70 | return tlv; 71 | } 72 | 73 | ALWAYS_INLINE operator T&(){ 74 | return *tlv; 75 | } 76 | }; 77 | 78 | 79 | } //namespace co -------------------------------------------------------------------------------- /src/test_mutex.cc: -------------------------------------------------------------------------------- 1 | //g++ -std=c++11 -llibgo test_mutex.cc 2 | 3 | #include 4 | #include "libgo/coroutine.h" 5 | #include "mutex.h" 6 | #include 7 | using namespace std; 8 | 9 | bool f(){ 10 | return true; 11 | } 12 | int main() 13 | { 14 | co::CoRecursiveMutex m; 15 | 16 | go [=]()mutable{ 17 | m.lock(); 18 | m.lock(); 19 | m.lock(); 20 | m.unlock(); 21 | m.unlock(); 22 | m.unlock(); 23 | cout << "g1" < | 16 | | Zeev Suraski | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifndef ZEND_PERMENENT_VARIABLES_H 23 | #define ZEND_PERMENENT_VARIABLES_H 24 | 25 | #define PHP_5_5_API_NO 220121212 26 | #define PHP_5_6_API_NO 220131226 27 | 28 | #define MAX_HASHTABLE_LAYERS (16) 29 | 30 | #if PHP_MAJOR_VERSION < 7 31 | #define ZVAL_PERSISTENT_PTR_DTOR (void (*)(void *)) zval_persistent_pptr_dtor_wrapper 32 | #else 33 | #define ZVAL_PERSISTENT_PTR_DTOR (void (*)(zval *)) zval_persistent_ptr_dtor_wrapper 34 | #endif 35 | 36 | #define zval_persistent_ptr_dtor(ppz) i_zval_persistent_ptr_dtor(*(ppz) ZEND_FILE_LINE_CC) 37 | 38 | 39 | ZEND_API void zval_persistent_copy_ctor(zval* zvalue); 40 | ZEND_API void _zval_persistent_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC); 41 | 42 | ZEND_API void zval_persistent_to_local_ptr_ctor(zval** zvalue); 43 | #define zval_persistent_to_local_copy_ctor(z) _zval_persistent_to_local_copy_ctor_func(z ZEND_FILE_LINE_CC); 44 | ZEND_API void _zval_persistent_to_local_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC); 45 | 46 | ZEND_API void zval_persistent_pptr_dtor_wrapper(zval **zval_ptr); 47 | ZEND_API void zval_persistent_ptr_dtor_wrapper(zval *zval_ptr); 48 | ZEND_API void _zval_persistent_dtor_func(zval *zvalue ZEND_FILE_LINE_DC); 49 | static zend_always_inline void _zval_persistent_dtor(zval *zvalue ZEND_FILE_LINE_DC); 50 | #define zval_persistent_dtor(z) _zval_persistent_dtor((z) ZEND_FILE_LINE_CC) 51 | 52 | #if PHP_MAJOR_VERSION < 7 53 | static zend_always_inline void i_zval_persistent_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) 54 | { 55 | if (!Z_DELREF_P(zval_ptr)) { 56 | _zval_persistent_dtor_func(zval_ptr ZEND_FILE_LINE_RELAY_CC); 57 | pefree_rel(zval_ptr, 1); 58 | } else { 59 | //TSRMLS_FETCH(); 60 | 61 | if (Z_REFCOUNT_P(zval_ptr) == 1) { 62 | Z_UNSET_ISREF_P(zval_ptr); 63 | } 64 | //todo: to rewrite GC_ZVAL_CHECK_POSSIBLE_ROOT for full 65 | //gc functionality on persistent memory 66 | //GC_ZVAL_CHECK_POSSIBLE_ROOT(zval_ptr); 67 | } 68 | } 69 | 70 | static zend_always_inline void _zval_persistent_dtor(zval *zvalue ZEND_FILE_LINE_DC) 71 | { 72 | if (Z_TYPE_P(zvalue) <= IS_BOOL) { 73 | return; 74 | } 75 | _zval_persistent_dtor_func(zvalue ZEND_FILE_LINE_RELAY_CC); 76 | } 77 | #else 78 | 79 | static zend_always_inline void i_zval_persistent_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) 80 | { 81 | if (Z_REFCOUNTED_P(zval_ptr)) { 82 | zend_refcounted *ref = Z_COUNTED_P(zval_ptr); 83 | if (!Z_DELREF_P(zval_ptr)) { 84 | _zval_persistent_dtor_func(zval_ptr ZEND_FILE_LINE_RELAY_CC); 85 | } else { 86 | //gc_check_possible_root(ref); 87 | } 88 | } 89 | } 90 | 91 | #endif 92 | 93 | #endif 94 | 95 | 96 | -------------------------------------------------------------------------------- /test_php: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export LD_PRELOAD=liblibgo.so 3 | php "$@" 4 | -------------------------------------------------------------------------------- /tests/001_phpgo_presence.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for phpgo presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | phpgo extension is available 22 | -------------------------------------------------------------------------------- /tests/002_go_hello_world.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go a hello world routine 3 | 4 | --FILE-- 5 | 15 | --EXPECT-- 16 | Hello World 17 | -------------------------------------------------------------------------------- /tests/003_go_named_function.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go named function 3 | 4 | --FILE-- 5 | 20 | --EXPECT-- 21 | Hello World 22 | Hello World 23 | -------------------------------------------------------------------------------- /tests/004_go_closure_with_args.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go closure with args 3 | 4 | --FILE-- 5 | value = $value; 81 | } 82 | public function say(){ 83 | echo $this->value . PHP_EOL; 84 | } 85 | }; 86 | 87 | $obj = new TestClass("this is test class"); 88 | 89 | //array argument 90 | 91 | go( 92 | function($arr){ 93 | subtc(11); 94 | //var_export($arr,false); 95 | //echo PHP_EOL; 96 | var_export($arr); 97 | $arr[2]->say(); 98 | }, 99 | [ 100 | ["key0"=>"0", 101 | "key1"=>1, 102 | "subArray"=> 103 | ["subsubArray"=> 104 | [ 1, 2, "3", false, null], 105 | "subkey0"=>0, 106 | "subkey1"=>"subvalue1", 107 | ], 108 | null, 109 | false, 110 | $obj, 111 | ] 112 | ] 113 | ); 114 | 115 | //array var argument 116 | $arr = 117 | ["key0"=>"0", 118 | "key1"=>1, 119 | "subArray"=> 120 | ["subsubArray"=> 121 | [ 1, 2, "3", false, null], 122 | "subkey0"=>0, 123 | "subkey1"=>"subvalue1", 124 | ], 125 | null, 126 | false, 127 | $obj, 128 | ]; 129 | 130 | go(function($arr){ 131 | subtc(12); 132 | var_export($arr); 133 | $arr[2]->say(); 134 | }, [$arr]); 135 | 136 | //array reference argument 137 | 138 | go(function(&$arr){ 139 | subtc(13); 140 | var_export($arr); 141 | $arr[2]->say(); 142 | }, [&$arr]); 143 | 144 | //array reference argument, modified 145 | go(function(&$arr){ 146 | subtc(14); 147 | $arr["subArray"]["subsubArray"][1] = "replaced"; 148 | }, [&$arr]); 149 | 150 | 151 | Scheduler::join(); 152 | subtc(15); 153 | var_export($arr); 154 | 155 | $i = 1; 156 | $bool = false; 157 | $null = null; 158 | $string = "string"; 159 | $obj = new TestClass("test class obj"); 160 | $arr = [1,2,3,4]; 161 | 162 | ini_set('memory_limit', '200M'); 163 | 164 | go(function($i, $bool, $null, $string, $obj, $arr, &$i1, &$bool1, &$null1, &$string1, &$obj1, &$arr1){ 165 | subtc(16); 166 | 167 | echo $i .PHP_EOL; 168 | echo $bool .PHP_EOL; 169 | echo $null .PHP_EOL; 170 | echo $string .PHP_EOL; 171 | var_export($obj); 172 | var_export ($arr); 173 | 174 | echo $i1 .PHP_EOL; 175 | echo $bool1 .PHP_EOL; 176 | echo $null1 .PHP_EOL; 177 | echo $string1 .PHP_EOL; 178 | var_export($obj1); 179 | var_export ($arr1); 180 | 181 | }, [1, true, null, "string", $obj, $arr, &$i, &$bool, &$null, &$string, &$obj, &$arr]); 182 | 183 | Scheduler::join(); 184 | 185 | ?> 186 | --EXPECT-- 187 | SUB-TC: #1 188 | Tony 189 | SUB-TC: #2 190 | Hello World 191 | SUB-TC: #3 192 | Hello World 193 | SUB-TC: #4 194 | Hello Tony 195 | SUB-TC: #5 196 | Hello Tony 197 | SUB-TC: #6 198 | Tony Hello 199 | SUB-TC: #6.1 200 | 100 201 | SUB-TC: #7 202 | Hello 100 203 | SUB-TC: #8 204 | Hello 100 205 | SUB-TC: #9 206 | Hello 100 207 | SUB-TC: #10 208 | Hello 100 209 | SUB-TC: #10.1 210 | -99887766 211 | SUB-TC: #11 212 | array ( 213 | 'key0' => '0', 214 | 'key1' => 1, 215 | 'subArray' => 216 | array ( 217 | 'subsubArray' => 218 | array ( 219 | 0 => 1, 220 | 1 => 2, 221 | 2 => '3', 222 | 3 => false, 223 | 4 => NULL, 224 | ), 225 | 'subkey0' => 0, 226 | 'subkey1' => 'subvalue1', 227 | ), 228 | 0 => NULL, 229 | 1 => false, 230 | 2 => 231 | TestClass::__set_state(array( 232 | 'value' => 'this is test class', 233 | )), 234 | )this is test class 235 | SUB-TC: #12 236 | array ( 237 | 'key0' => '0', 238 | 'key1' => 1, 239 | 'subArray' => 240 | array ( 241 | 'subsubArray' => 242 | array ( 243 | 0 => 1, 244 | 1 => 2, 245 | 2 => '3', 246 | 3 => false, 247 | 4 => NULL, 248 | ), 249 | 'subkey0' => 0, 250 | 'subkey1' => 'subvalue1', 251 | ), 252 | 0 => NULL, 253 | 1 => false, 254 | 2 => 255 | TestClass::__set_state(array( 256 | 'value' => 'this is test class', 257 | )), 258 | )this is test class 259 | SUB-TC: #13 260 | array ( 261 | 'key0' => '0', 262 | 'key1' => 1, 263 | 'subArray' => 264 | array ( 265 | 'subsubArray' => 266 | array ( 267 | 0 => 1, 268 | 1 => 2, 269 | 2 => '3', 270 | 3 => false, 271 | 4 => NULL, 272 | ), 273 | 'subkey0' => 0, 274 | 'subkey1' => 'subvalue1', 275 | ), 276 | 0 => NULL, 277 | 1 => false, 278 | 2 => 279 | TestClass::__set_state(array( 280 | 'value' => 'this is test class', 281 | )), 282 | )this is test class 283 | SUB-TC: #14 284 | SUB-TC: #15 285 | array ( 286 | 'key0' => '0', 287 | 'key1' => 1, 288 | 'subArray' => 289 | array ( 290 | 'subsubArray' => 291 | array ( 292 | 0 => 1, 293 | 1 => 'replaced', 294 | 2 => '3', 295 | 3 => false, 296 | 4 => NULL, 297 | ), 298 | 'subkey0' => 0, 299 | 'subkey1' => 'subvalue1', 300 | ), 301 | 0 => NULL, 302 | 1 => false, 303 | 2 => 304 | TestClass::__set_state(array( 305 | 'value' => 'this is test class', 306 | )), 307 | )SUB-TC: #16 308 | 1 309 | 1 310 | 311 | string 312 | TestClass::__set_state(array( 313 | 'value' => 'test class obj', 314 | ))array ( 315 | 0 => 1, 316 | 1 => 2, 317 | 2 => 3, 318 | 3 => 4, 319 | )1 320 | 321 | 322 | string 323 | TestClass::__set_state(array( 324 | 'value' => 'test class obj', 325 | ))array ( 326 | 0 => 1, 327 | 1 => 2, 328 | 2 => 3, 329 | 3 => 4, 330 | ) -------------------------------------------------------------------------------- /tests/005_go_closure_with_128_args.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go closure with 128 args 3 | 4 | --FILE-- 5 | 44 | --EXPECT-- 45 | SUB-TC: #1 46 | array(128) { 47 | [0]=> 48 | int(1) 49 | [1]=> 50 | int(2) 51 | [2]=> 52 | int(3) 53 | [3]=> 54 | int(4) 55 | [4]=> 56 | int(5) 57 | [5]=> 58 | int(6) 59 | [6]=> 60 | int(7) 61 | [7]=> 62 | int(8) 63 | [8]=> 64 | int(9) 65 | [9]=> 66 | int(10) 67 | [10]=> 68 | int(11) 69 | [11]=> 70 | int(12) 71 | [12]=> 72 | int(13) 73 | [13]=> 74 | int(14) 75 | [14]=> 76 | int(15) 77 | [15]=> 78 | int(16) 79 | [16]=> 80 | int(1) 81 | [17]=> 82 | int(2) 83 | [18]=> 84 | int(3) 85 | [19]=> 86 | int(4) 87 | [20]=> 88 | int(5) 89 | [21]=> 90 | int(6) 91 | [22]=> 92 | int(7) 93 | [23]=> 94 | int(8) 95 | [24]=> 96 | int(9) 97 | [25]=> 98 | int(10) 99 | [26]=> 100 | int(11) 101 | [27]=> 102 | int(12) 103 | [28]=> 104 | int(13) 105 | [29]=> 106 | int(14) 107 | [30]=> 108 | int(15) 109 | [31]=> 110 | int(16) 111 | [32]=> 112 | int(1) 113 | [33]=> 114 | int(2) 115 | [34]=> 116 | int(3) 117 | [35]=> 118 | int(4) 119 | [36]=> 120 | int(5) 121 | [37]=> 122 | int(6) 123 | [38]=> 124 | int(7) 125 | [39]=> 126 | int(8) 127 | [40]=> 128 | int(9) 129 | [41]=> 130 | int(10) 131 | [42]=> 132 | int(11) 133 | [43]=> 134 | int(12) 135 | [44]=> 136 | int(13) 137 | [45]=> 138 | int(14) 139 | [46]=> 140 | int(15) 141 | [47]=> 142 | int(16) 143 | [48]=> 144 | int(1) 145 | [49]=> 146 | int(2) 147 | [50]=> 148 | int(3) 149 | [51]=> 150 | int(4) 151 | [52]=> 152 | int(5) 153 | [53]=> 154 | int(6) 155 | [54]=> 156 | int(7) 157 | [55]=> 158 | int(8) 159 | [56]=> 160 | int(9) 161 | [57]=> 162 | int(10) 163 | [58]=> 164 | int(11) 165 | [59]=> 166 | int(12) 167 | [60]=> 168 | int(13) 169 | [61]=> 170 | int(14) 171 | [62]=> 172 | int(15) 173 | [63]=> 174 | int(16) 175 | [64]=> 176 | int(1) 177 | [65]=> 178 | int(2) 179 | [66]=> 180 | int(3) 181 | [67]=> 182 | int(4) 183 | [68]=> 184 | int(5) 185 | [69]=> 186 | int(6) 187 | [70]=> 188 | int(7) 189 | [71]=> 190 | int(8) 191 | [72]=> 192 | int(9) 193 | [73]=> 194 | int(10) 195 | [74]=> 196 | int(11) 197 | [75]=> 198 | int(12) 199 | [76]=> 200 | int(13) 201 | [77]=> 202 | int(14) 203 | [78]=> 204 | int(15) 205 | [79]=> 206 | int(16) 207 | [80]=> 208 | int(1) 209 | [81]=> 210 | int(2) 211 | [82]=> 212 | int(3) 213 | [83]=> 214 | int(4) 215 | [84]=> 216 | int(5) 217 | [85]=> 218 | int(6) 219 | [86]=> 220 | int(7) 221 | [87]=> 222 | int(8) 223 | [88]=> 224 | int(9) 225 | [89]=> 226 | int(10) 227 | [90]=> 228 | int(11) 229 | [91]=> 230 | int(12) 231 | [92]=> 232 | int(13) 233 | [93]=> 234 | int(14) 235 | [94]=> 236 | int(15) 237 | [95]=> 238 | int(16) 239 | [96]=> 240 | int(1) 241 | [97]=> 242 | int(2) 243 | [98]=> 244 | int(3) 245 | [99]=> 246 | int(4) 247 | [100]=> 248 | int(5) 249 | [101]=> 250 | int(6) 251 | [102]=> 252 | int(7) 253 | [103]=> 254 | int(8) 255 | [104]=> 256 | int(9) 257 | [105]=> 258 | int(10) 259 | [106]=> 260 | int(11) 261 | [107]=> 262 | int(12) 263 | [108]=> 264 | int(13) 265 | [109]=> 266 | int(14) 267 | [110]=> 268 | int(15) 269 | [111]=> 270 | int(16) 271 | [112]=> 272 | int(1) 273 | [113]=> 274 | int(2) 275 | [114]=> 276 | int(3) 277 | [115]=> 278 | int(4) 279 | [116]=> 280 | int(5) 281 | [117]=> 282 | int(6) 283 | [118]=> 284 | int(7) 285 | [119]=> 286 | int(8) 287 | [120]=> 288 | int(9) 289 | [121]=> 290 | int(10) 291 | [122]=> 292 | int(11) 293 | [123]=> 294 | int(12) 295 | [124]=> 296 | int(13) 297 | [125]=> 298 | int(14) 299 | [126]=> 300 | int(15) 301 | [127]=> 302 | int(16) 303 | } -------------------------------------------------------------------------------- /tests/007_go_closure_with_256_local_vars.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go closure with 256 local variables 3 | 4 | --FILE-- 5 | 48 | --EXPECT-- 49 | SUB-TC: #1 50 | array(2) { 51 | [0]=> 52 | int(1) 53 | [1]=> 54 | int(2) 55 | } 56 | object(TestClass)#2 (0) { 57 | } 58 | int(1) 59 | bool(false) 60 | bool(true) 61 | 11 62 | -------------------------------------------------------------------------------- /tests/009_go_recursive_function_128_levels.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go recursive function with 128 nested levels 3 | 4 | --FILE-- 5 | 48 | --EXPECT-- 49 | the only output 50 | -------------------------------------------------------------------------------- /tests/010_go_recursive_function_4096_levels.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go recursive function with 4096 nested levels (deemed to be FAIL) 3 | 4 | --SKIPIF-- 5 | 8 | 9 | --FILE-- 10 | 53 | --EXPECT-- 54 | the only output 55 | -------------------------------------------------------------------------------- /tests/011_go_routines_of_16_nested_levels.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go nested routines of 10240 levels 3 | 4 | --FILE-- 5 | 49 | --EXPECT-- 50 | the only output 51 | -------------------------------------------------------------------------------- /tests/012_go_routines_of_10240_concurrence.phpt__: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go concurrent 64k routines (192M memory limit ~ 3K per go) 3 | 4 | --FILE-- 5 | 50 | --EXPECT-- 51 | the only output 52 | -------------------------------------------------------------------------------- /tests/021_go_chan_create.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go channel create 3 | 4 | --FILE-- 5 | name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 16 | 17 | subtc(2); 18 | $ch = new Chan(0); 19 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 20 | 21 | subtc(3); 22 | $ch = new Chan(10000); 23 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 24 | 25 | subtc(4); 26 | $ch = new Chan(['name'=>"chan", "capacity"=>100, "copy"=>false]); 27 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 28 | 29 | subtc(5); 30 | $ch = new Chan(['name'=>"chan", "capacity"=>0]); 31 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 32 | 33 | subtc(6); 34 | $ch = new Chan(['name'=>"chan", "copy"=>false]); 35 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 36 | 37 | subtc(7); 38 | $ch = new Chan(["capacity"=>100, "copy"=>true]); 39 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 40 | 41 | subtc(8); 42 | $ch = new Chan(['name'=>"chan"]); 43 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 44 | 45 | subtc(9); 46 | $ch = new Chan(['name'=>""]); 47 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 48 | 49 | subtc(10); 50 | $ch = new Chan(['name'=>"123"]); 51 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 52 | 53 | subtc(11); 54 | $ch = new Chan(["capacity"=>0]); 55 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 56 | 57 | subtc(12); 58 | $ch = new Chan(["capacity"=>100]); 59 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 60 | 61 | subtc(13); 62 | $ch = new Chan(["copy"=>false]); 63 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 64 | 65 | subtc(14); 66 | $ch = new Chan(["copy"=>true]); 67 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 68 | 69 | subtc(15); 70 | $ch = new Chan([]); 71 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 72 | 73 | subtc(16); 74 | $ch = new Chan(["abc"=>"d"]); 75 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 76 | 77 | subtc(17); 78 | $ch = new Chan(["abc"=>"d", "name"=>"chan"]); 79 | echo "name: {$ch->name}; capacity {$ch->capacity}; copy ".var_export($ch->copy, true)."\n"; 80 | ?> 81 | --EXPECT-- 82 | SUB-TC: #1 83 | name: ; capacity 0; copy false 84 | SUB-TC: #2 85 | name: ; capacity 0; copy false 86 | SUB-TC: #3 87 | name: ; capacity 10000; copy false 88 | SUB-TC: #4 89 | name: chan; capacity 100; copy false 90 | SUB-TC: #5 91 | name: chan; capacity 0; copy false 92 | SUB-TC: #6 93 | name: chan; capacity 0; copy false 94 | SUB-TC: #7 95 | name: ; capacity 100; copy true 96 | SUB-TC: #8 97 | name: chan; capacity 0; copy false 98 | SUB-TC: #9 99 | name: ; capacity 0; copy false 100 | SUB-TC: #10 101 | name: 123; capacity 0; copy false 102 | SUB-TC: #11 103 | name: ; capacity 0; copy false 104 | SUB-TC: #12 105 | name: ; capacity 100; copy false 106 | SUB-TC: #13 107 | name: ; capacity 0; copy false 108 | SUB-TC: #14 109 | name: ; capacity 0; copy true 110 | SUB-TC: #15 111 | name: ; capacity 0; copy false 112 | SUB-TC: #16 113 | name: ; capacity 0; copy false 114 | SUB-TC: #17 115 | name: chan; capacity 0; copy false -------------------------------------------------------------------------------- /tests/022_go_chan_create_error_1.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go channel create error Chan(-1) 3 | 4 | --FILE-- 5 | 17 | --EXPECTREGEX-- 18 | .*the capacity must be greater than or equal to 0.* -------------------------------------------------------------------------------- /tests/023_go_chan_create_error_2.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go channel create error Chan(888888888888888888888888888888888888888) 3 | 4 | --FILE-- 5 | 17 | --EXPECTREGEX-- 18 | .*parameter 1 must be long or array in.* -------------------------------------------------------------------------------- /tests/024_go_chan_create_error_3.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go channel create error Chan(["name"=>1]) 3 | 4 | --FILE-- 5 | 1]); 15 | 16 | ?> 17 | --EXPECTREGEX-- 18 | .*option \"name\" must be string in.* -------------------------------------------------------------------------------- /tests/025_go_chan_create_error_4.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go channel create error Chan(["capacity"=>-1]) 3 | 4 | --FILE-- 5 | -1]); 15 | 16 | ?> 17 | --EXPECTREGEX-- 18 | .*the capacity must be greater than or equal to 0.* -------------------------------------------------------------------------------- /tests/026_go_chan_create_error_5.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go channel create error Chan(["copy"=>"true"]) 3 | 4 | --FILE-- 5 | "true"]); 15 | 16 | ?> 17 | --EXPECTREGEX-- 18 | .*option \"copy\" must be bool in.* -------------------------------------------------------------------------------- /tests/027_go_chan_create_error_6.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go channel create error Chan(["capacity"=>"100"]) 3 | 4 | --FILE-- 5 | "100"]); 15 | 16 | ?> 17 | --EXPECTREGEX-- 18 | .*option \"capacity\" must be long.* -------------------------------------------------------------------------------- /tests/029_go_chan_pop_outside_of_go_routine.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan pop outside of go routine 3 | 4 | --FILE-- 5 | push(100); 18 | },[$ch]); 19 | 20 | var_dump($ch->pop()); 21 | 22 | subtc(2); 23 | $ch = new Chan(0); 24 | 25 | go(function($ch){ 26 | sleep(0.1); 27 | $ch->push("abc"); 28 | },[$ch]); 29 | 30 | var_dump($ch->pop()); 31 | 32 | 33 | ?> 34 | --EXPECT-- 35 | SUB-TC: #1 36 | int(100) 37 | SUB-TC: #2 38 | string(3) "abc" 39 | -------------------------------------------------------------------------------- /tests/030_go_chan_1_push_pop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan(1) push pop 3 | 4 | --FILE-- 5 | pop(); 19 | echo "popped:"; 20 | var_dump($v); 21 | },[$ch]); 22 | 23 | go(function($ch){ 24 | echo "push\n"; 25 | $v = "abc"; 26 | $ch->push($v); 27 | echo "pushed $v\n"; 28 | },[$ch]); 29 | 30 | Scheduler::join(); 31 | 32 | ?> 33 | --EXPECT-- 34 | SUB-TC: #1 35 | pop 36 | push 37 | pushed abc 38 | popped:string(3) "abc" 39 | 40 | -------------------------------------------------------------------------------- /tests/031_go_chan_0_push_pop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan(0) push pop 3 | 4 | --FILE-- 5 | push($v); 20 | echo "pushed $v\n"; 21 | $ch->push(100); 22 | echo "pushed 100\n"; 23 | $ch->push(true); 24 | echo "pushed true\n"; 25 | $ch->push(false); 26 | echo "pushed false\n"; 27 | },[$ch]); 28 | 29 | go(function($ch){ 30 | echo "pop\n"; 31 | $v = $ch->pop(); 32 | echo "popped:"; 33 | var_dump($v); 34 | $v = $ch->pop(); 35 | echo "popped:"; 36 | var_dump($v); 37 | $v = $ch->pop(); 38 | echo "popped:"; 39 | var_dump($v); 40 | $v = $ch->pop(); 41 | echo "popped:"; 42 | var_dump($v); 43 | },[$ch]); 44 | 45 | Scheduler::join(); 46 | 47 | ?> 48 | --EXPECT-- 49 | SUB-TC: #1 50 | push 51 | pop 52 | pushed abc 53 | popped:string(3) "abc" 54 | pushed 100 55 | popped:int(100) 56 | pushed true 57 | popped:bool(true) 58 | pushed false 59 | popped:bool(false) 60 | 61 | -------------------------------------------------------------------------------- /tests/032_go_chan_push_pop_array.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan push pop array 3 | 4 | --FILE-- 5 | "d", 3, 4, "subarr"=>[1,2,"c"=>"d"] ]; 19 | $ch->push($v); 20 | echo "pushed:"; 21 | var_dump($v); 22 | },[$ch]); 23 | 24 | go(function($ch){ 25 | echo "pop\n"; 26 | $v = $ch->pop(); 27 | echo "popped:"; 28 | var_dump($v); 29 | },[$ch]); 30 | 31 | Scheduler::join(); 32 | 33 | ?> 34 | --EXPECT-- 35 | SUB-TC: #1 36 | push 37 | pushed:array(4) { 38 | ["abc"]=> 39 | string(1) "d" 40 | [0]=> 41 | int(3) 42 | [1]=> 43 | int(4) 44 | ["subarr"]=> 45 | array(3) { 46 | [0]=> 47 | int(1) 48 | [1]=> 49 | int(2) 50 | ["c"]=> 51 | string(1) "d" 52 | } 53 | } 54 | pop 55 | popped:array(4) { 56 | ["abc"]=> 57 | string(1) "d" 58 | [0]=> 59 | int(3) 60 | [1]=> 61 | int(4) 62 | ["subarr"]=> 63 | array(3) { 64 | [0]=> 65 | int(1) 66 | [1]=> 67 | int(2) 68 | ["c"]=> 69 | string(1) "d" 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /tests/033_go_chan_push_pop_object.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan push pop object 3 | 4 | --FILE-- 5 | name = $name; 18 | } 19 | public function say(){ 20 | echo "I am {$this->name}!\n"; 21 | } 22 | } 23 | 24 | subtc(1); 25 | $ch = new Chan(1); 26 | 27 | go(function($ch){ 28 | echo "push\n"; 29 | $v = new ObjectClass("testObject"); 30 | $v->say(); 31 | $ch->push($v); 32 | echo "pushed:"; 33 | var_dump($v); 34 | },[$ch]); 35 | 36 | go(function($ch){ 37 | echo "pop\n"; 38 | $v = $ch->pop(); 39 | echo "popped:"; 40 | var_dump($v); 41 | $v->say(); 42 | },[$ch]); 43 | 44 | Scheduler::join(); 45 | 46 | ?> 47 | --EXPECT-- 48 | SUB-TC: #1 49 | push 50 | I am testObject! 51 | pushed:object(ObjectClass)#4 (1) { 52 | ["name":"ObjectClass":private]=> 53 | string(10) "testObject" 54 | } 55 | pop 56 | popped:object(ObjectClass)#4 (1) { 57 | ["name":"ObjectClass":private]=> 58 | string(10) "testObject" 59 | } 60 | I am testObject! 61 | 62 | 63 | -------------------------------------------------------------------------------- /tests/034_go_chan_1_copy_push_pop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan(1,copy) push pop 3 | 4 | --FILE-- 5 | 1, "copy"=>true]); 15 | 16 | go(function($ch){ 17 | $v = "abc"; 18 | 19 | echo "go1: try push $v\n"; 20 | $ch->push($v); 21 | echo "go1: pushed $v\n"; 22 | 23 | echo "go1: try push 100\n"; 24 | $ch->push(100); 25 | echo "go1: pushed 100\n"; 26 | 27 | echo "go1: try push true\n"; 28 | $ch->push(true); 29 | echo "go1: pushed true\n"; 30 | 31 | echo "go1: try push false\n"; 32 | $ch->push(false); 33 | echo "go1: pushed false\n"; 34 | },[$ch]); 35 | 36 | go(function($ch){ 37 | echo "go2: try pop\n"; 38 | $v = $ch->pop(); 39 | echo "go2: popped:"; var_dump($v); 40 | 41 | echo "go2: try pop\n"; 42 | $v = $ch->pop(); 43 | echo "go2: popped:"; var_dump($v); 44 | 45 | echo "go2: try pop\n"; 46 | $v = $ch->pop(); 47 | echo "go2: popped:"; var_dump($v); 48 | 49 | echo "go2: try pop\n"; 50 | $v = $ch->pop(); 51 | echo "go2: popped:"; var_dump($v); 52 | },[$ch]); 53 | 54 | Scheduler::join(); 55 | 56 | ?> 57 | --EXPECT-- 58 | SUB-TC: #1 59 | go1: try push abc 60 | go1: pushed abc 61 | go1: try push 100 62 | go2: try pop 63 | go2: popped:string(3) "abc" 64 | go2: try pop 65 | go1: pushed 100 66 | go1: try push true 67 | go1: pushed true 68 | go1: try push false 69 | go2: popped:int(100) 70 | go2: try pop 71 | go2: popped:bool(true) 72 | go2: try pop 73 | go1: pushed false 74 | go2: popped:bool(false) 75 | -------------------------------------------------------------------------------- /tests/035_go_chan_0_copy_push_pop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan(0,copy) push pop 3 | 4 | --FILE-- 5 | 0, "copy"=>true]); 15 | 16 | go(function($ch){ 17 | echo "push\n"; 18 | $v = "abc"; 19 | $ch->push($v); 20 | echo "pushed $v\n"; 21 | $ch->push(100); 22 | echo "pushed 100\n"; 23 | $ch->push(true); 24 | echo "pushed true\n"; 25 | $ch->push(false); 26 | echo "pushed false\n"; 27 | },[$ch]); 28 | 29 | go(function($ch){ 30 | echo "pop\n"; 31 | $v = $ch->pop(); 32 | echo "popped:"; 33 | var_dump($v); 34 | $v = $ch->pop(); 35 | echo "popped:"; 36 | var_dump($v); 37 | $v = $ch->pop(); 38 | echo "popped:"; 39 | var_dump($v); 40 | $v = $ch->pop(); 41 | echo "popped:"; 42 | var_dump($v); 43 | },[$ch]); 44 | 45 | Scheduler::join(); 46 | 47 | ?> 48 | --EXPECT-- 49 | SUB-TC: #1 50 | push 51 | pop 52 | pushed abc 53 | popped:string(3) "abc" 54 | pushed 100 55 | popped:int(100) 56 | pushed true 57 | popped:bool(true) 58 | pushed false 59 | popped:bool(false) 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /tests/036_go_chan_10_copy_push_pop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan(10,copy) push pop 3 | 4 | --FILE-- 5 | 10, "copy"=>true]); 15 | 16 | go(function($ch){ 17 | for($i = 1; $i<=20; $i++){ 18 | echo "push $i:\n"; 19 | $ch->push($i); 20 | } 21 | },[$ch]); 22 | 23 | go(function($ch){ 24 | $i = 1; 25 | while($i <= 20){ 26 | echo "pop for the $i time:\n"; 27 | var_dump($ch->pop()); 28 | $i++; 29 | } 30 | },[$ch]); 31 | 32 | Scheduler::join(); 33 | 34 | ?> 35 | --EXPECT-- 36 | SUB-TC: #1 37 | push 1: 38 | push 2: 39 | push 3: 40 | push 4: 41 | push 5: 42 | push 6: 43 | push 7: 44 | push 8: 45 | push 9: 46 | push 10: 47 | push 11: 48 | pop for the 1 time: 49 | int(1) 50 | pop for the 2 time: 51 | int(2) 52 | pop for the 3 time: 53 | int(3) 54 | pop for the 4 time: 55 | int(4) 56 | pop for the 5 time: 57 | int(5) 58 | pop for the 6 time: 59 | int(6) 60 | pop for the 7 time: 61 | int(7) 62 | pop for the 8 time: 63 | int(8) 64 | pop for the 9 time: 65 | int(9) 66 | pop for the 10 time: 67 | int(10) 68 | pop for the 11 time: 69 | push 12: 70 | push 13: 71 | push 14: 72 | push 15: 73 | push 16: 74 | push 17: 75 | push 18: 76 | push 19: 77 | push 20: 78 | int(11) 79 | pop for the 12 time: 80 | int(12) 81 | pop for the 13 time: 82 | int(13) 83 | pop for the 14 time: 84 | int(14) 85 | pop for the 15 time: 86 | int(15) 87 | pop for the 16 time: 88 | int(16) 89 | pop for the 17 time: 90 | int(17) 91 | pop for the 18 time: 92 | int(18) 93 | pop for the 19 time: 94 | int(19) 95 | pop for the 20 time: 96 | int(20) 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /tests/037_go_chan_10_push_pop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan(10) push pop 3 | 4 | --FILE-- 5 | 10]); 15 | 16 | go(function($ch){ 17 | for($i = 1; $i<=20; $i++){ 18 | echo "push $i:\n"; 19 | $ch->push($i); 20 | } 21 | },[$ch]); 22 | 23 | go(function($ch){ 24 | $i = 1; 25 | while($i <= 20){ 26 | echo "pop for the $i time:\n"; 27 | var_dump($ch->pop()); 28 | $i++; 29 | } 30 | },[$ch]); 31 | 32 | Scheduler::join(); 33 | 34 | ?> 35 | --EXPECT-- 36 | SUB-TC: #1 37 | push 1: 38 | push 2: 39 | push 3: 40 | push 4: 41 | push 5: 42 | push 6: 43 | push 7: 44 | push 8: 45 | push 9: 46 | push 10: 47 | push 11: 48 | pop for the 1 time: 49 | int(1) 50 | pop for the 2 time: 51 | int(2) 52 | pop for the 3 time: 53 | int(3) 54 | pop for the 4 time: 55 | int(4) 56 | pop for the 5 time: 57 | int(5) 58 | pop for the 6 time: 59 | int(6) 60 | pop for the 7 time: 61 | int(7) 62 | pop for the 8 time: 63 | int(8) 64 | pop for the 9 time: 65 | int(9) 66 | pop for the 10 time: 67 | int(10) 68 | pop for the 11 time: 69 | push 12: 70 | push 13: 71 | push 14: 72 | push 15: 73 | push 16: 74 | push 17: 75 | push 18: 76 | push 19: 77 | push 20: 78 | int(11) 79 | pop for the 12 time: 80 | int(12) 81 | pop for the 13 time: 82 | int(13) 83 | pop for the 14 time: 84 | int(14) 85 | pop for the 15 time: 86 | int(15) 87 | pop for the 16 time: 88 | int(16) 89 | pop for the 17 time: 90 | int(17) 91 | pop for the 18 time: 92 | int(18) 93 | pop for the 19 time: 94 | int(19) 95 | pop for the 20 time: 96 | int(20) 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /tests/038_go_chan_trypush_trypop_close.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Chan tryPush tryPop close 3 | 4 | --FILE-- 5 | tryPush("push#0") ); 33 | 34 | go(function($ch){ 35 | $push = 0; 36 | my_assert(1, !$ch->tryPush("push#1")); 37 | while( !$ch->tryPush("push#1") ){ 38 | $push++; 39 | Time::sleep(100 * Time::MILLISECOND); 40 | } 41 | my_assert(1, $push == 10); 42 | my_assert(1, $ch->tryPop() == "push#1"); 43 | },[$ch]); 44 | 45 | go(function($ch){ 46 | Time::sleep(1 * Time::SECOND); 47 | my_assert(1, $ch->pop() == "push#0" ); 48 | },[$ch]); 49 | 50 | Scheduler::join(); 51 | passtc(1, true); 52 | 53 | //=======> 54 | subtc(2, "chan close test - ensure data can be read from close channel if available"); 55 | $ch = new Chan(1); 56 | 57 | go(function($ch){ 58 | $v = $ch->pop(); 59 | my_assert(2, $v==="push#0"); 60 | },[$ch]); 61 | 62 | my_assert(2, $ch->push("push#0")); 63 | $ch->close(); 64 | 65 | Scheduler::join(); 66 | passtc(2, true); 67 | 68 | //===> 69 | 70 | subtc(3, "chan close test - ensure warning generated while tryPushing to closed channel and NULL returned"); 71 | $ch1 = new Chan(0); 72 | 73 | go(function($ch){ 74 | @$res = $ch->tryPush("push#1"); 75 | my_assert(3, $res===null); 76 | },[$ch1]); 77 | $ch1->close(); 78 | 79 | Scheduler::join(); 80 | passtc(3, true); 81 | 82 | 83 | //=======> 84 | subtc(4, "chan close test - ensure data can be tryPop from close channel if available"); 85 | $ch = new Chan(1); 86 | 87 | go(function($ch){ 88 | $v = $ch->tryPop(); 89 | my_assert(4, $v==="push#0"); 90 | },[$ch]); 91 | 92 | $ch->push("push#0"); 93 | $ch->close(); 94 | 95 | Scheduler::join(); 96 | passtc(4, true); 97 | 98 | //=======> 99 | subtc(5, "chan close test - ensure NULL retuned for tryPop if channel closed and no data ready"); 100 | $ch = new Chan(1); 101 | 102 | go(function($ch){ 103 | $v = $ch->tryPop(); 104 | my_assert(5, $v===NULL); 105 | },[$ch]); 106 | 107 | $ch->close(); 108 | 109 | Scheduler::join(); 110 | passtc(5, true); 111 | 112 | //=======> 113 | subtc(6, "chan close test - read closed channel from outside go routine - data not available - chan(0)"); 114 | $ch = new Chan(0); 115 | $ch->close(); 116 | 117 | $v = $ch->Pop(); 118 | passtc(6, $v===NULL); 119 | 120 | //=======> 121 | subtc(7, "chan close test - read closed channel from outside go routine - data not available - chan(1)"); 122 | $ch = new Chan(1); 123 | $ch->close(); 124 | 125 | $v = $ch->Pop(); 126 | passtc(7, $v===NULL); 127 | 128 | //=======> 129 | subtc(8, "chan close test - read closed channel from outside go routine - data available - chan(1)"); 130 | $ch = new Chan(1); 131 | $ch->Push("abc"); 132 | $ch->close(); 133 | 134 | $v = $ch->Pop(); 135 | passtc(8, $v==="abc"); 136 | 137 | ?> 138 | --EXPECT-- 139 | SUB-TC #1: tryPush tryPop test 140 | SUB-TC #1: PASS 141 | SUB-TC #2: chan close test - ensure data can be read from close channel if available 142 | SUB-TC #2: PASS 143 | SUB-TC #3: chan close test - ensure warning generated while tryPushing to closed channel and NULL returned 144 | SUB-TC #3: PASS 145 | SUB-TC #4: chan close test - ensure data can be tryPop from close channel if available 146 | SUB-TC #4: PASS 147 | SUB-TC #5: chan close test - ensure NULL retuned for tryPop if channel closed and no data ready 148 | SUB-TC #5: PASS 149 | SUB-TC #6: chan close test - read closed channel from outside go routine - data not available - chan(0) 150 | SUB-TC #6: PASS 151 | SUB-TC #7: chan close test - read closed channel from outside go routine - data not available - chan(1) 152 | SUB-TC #7: PASS 153 | SUB-TC #8: chan close test - read closed channel from outside go routine - data available - chan(1) 154 | SUB-TC #8: PASS 155 | 156 | 157 | -------------------------------------------------------------------------------- /tests/039_go_chan_close_warnings.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go chan close warnings test 3 | 4 | --FILE-- 5 | 31 | subtc(1, "chan close test - ensure warning generated while pushing to closed channel and NULL returned"); 32 | $ch1 = new Chan(0); 33 | 34 | go(function($ch){ 35 | $res = $ch->push("push#1"); 36 | my_assert(1, $res===null); 37 | },[$ch1]); 38 | $ch1->close(); 39 | 40 | Scheduler::join(); 41 | passtc(1, true); 42 | 43 | subtc(2, "chan close test - ensure warning generated while tryPushing to closed channel and NULL returned"); 44 | $ch1 = new Chan(0); 45 | 46 | go(function($ch){ 47 | $res = $ch->tryPush("push#1"); 48 | my_assert(2, $res===null); 49 | },[$ch1]); 50 | $ch1->close(); 51 | 52 | Scheduler::join(); 53 | passtc(2, true); 54 | 55 | ?> 56 | --EXPECTREGEX-- 57 | .*SUB-TC #1.*allready closed.*SUB-TC #1: PASS.*SUB-TC #2.*allready closed.*SUB-TC #2: PASS.* 58 | -------------------------------------------------------------------------------- /tests/040_go_select_read_select_write.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select read and select write 3 | 4 | --FILE-- 5 | 10]); 31 | 32 | go(function($ch){ 33 | for($i = 1; $i<=20; $i++){ 34 | //echo "push $i:\n"; 35 | $ch->push($i); 36 | } 37 | },[$ch]); 38 | 39 | 40 | go(function() use($ch){ 41 | $v = -1; $u = $v; $i=1; 42 | $cases = ['case', 'CasE', 'CAse', 'CASe', 'CASE']; 43 | while(true){ 44 | select( 45 | [ 46 | $cases[$i%5], $ch, "->", &$v, function($v) use($i){ 47 | my_assert(1, $v==$i); 48 | } 49 | ] 50 | ); 51 | if($i>=20) break; 52 | $i++; 53 | usleep(100); 54 | } 55 | passtc(1, $v == 20 && $u = -1); 56 | }); 57 | 58 | Scheduler::join(); 59 | //passtc(1, $v == 20 && $u = -1); 60 | 61 | subtc(2, "Verify reading string from channel into a reference"); 62 | $ch = new Chan(["capacity"=>10]); 63 | 64 | go(function($ch){ 65 | for($i = 1; $i<=20; $i++){ 66 | //echo "push $i:\n"; 67 | $ch->push("$i"); 68 | } 69 | },[$ch]); 70 | 71 | 72 | go(function() use($ch){ 73 | $v = -1; $u = $v; $i=1; 74 | $cases = ['case', 'CasE', 'CAse', 'CASe', 'CASE']; 75 | while(true){ 76 | select( 77 | [ 78 | $cases[$i%5], $ch, "->", &$v, function($v) use($i){ 79 | my_assert(2, $v==$i); 80 | } 81 | ] 82 | ); 83 | if($i>=20) break; 84 | $i++; 85 | usleep(100); 86 | } 87 | passtc(2, $v == 20 && $u = -1); 88 | }); 89 | 90 | Scheduler::join(); 91 | 92 | 93 | subtc(3, "Verify reading array from channel into a reference"); 94 | $ch = new Chan(["capacity"=>10]); 95 | 96 | go(function($ch){ 97 | for($i = 1; $i<=20; $i++){ 98 | //echo "push $i:\n"; 99 | $ch->push( array("value"=>$i) ); 100 | } 101 | },[$ch]); 102 | 103 | go(function() use($ch){ 104 | $v = -1; $u = $v; $i=1; 105 | $cases = ['case', 'CasE', 'CAse', 'CASe', 'CASE']; 106 | while(true){ 107 | select( 108 | [ 109 | $cases[$i%5], $ch, "->", &$v, function($v) use($i){ 110 | my_assert(3, $v== array("value"=>$i) ); 111 | } 112 | ] 113 | ); 114 | if($i>=20) break; 115 | $i++; 116 | usleep(100); 117 | } 118 | passtc(3, $v == array("value"=>20) && $u = -1); 119 | }); 120 | 121 | Scheduler::join(); 122 | 123 | 124 | $ch = new Chan(["capacity"=>10]); 125 | 126 | subtc(4, "Verify select write different data types into channel"); 127 | go(function($ch){ 128 | for($i = 0; $i<9; $i++){ 129 | //echo "push $i:\n"; 130 | $ch->pop(); 131 | } 132 | },[$ch]); 133 | 134 | 135 | go(function() use($ch){ 136 | $v = -1; $u = $v; $i=0; 137 | class A{ 138 | public $a; 139 | public function say(){ 140 | echo "i am A\n"; 141 | } 142 | }; 143 | $obj = new A(); 144 | @$to_write = [ 145 | 0, 100, "string", array(), array("key"=>1), $obj, 146 | null, $undefined, function(){ echo "i am lamda\n"; }, 147 | ]; 148 | while($i < 9){ 149 | select( 150 | [ 151 | 'case', $ch, "<-", $to_write[$i % count($to_write)], function($v) use($i){ 152 | echo "#$i var written :\n"; 153 | var_export($v); echo "\n"; 154 | if( is_object($v) and !is_callable($v) ){ 155 | $v->say(); 156 | } 157 | if( is_callable($v) ){ 158 | $v(); 159 | } 160 | } 161 | ] 162 | ); 163 | $i++; 164 | usleep(100); 165 | } 166 | }); 167 | 168 | Scheduler::join(); 169 | 170 | ?> 171 | --EXPECT-- 172 | SUB-TC #1: Verify reading int from channel into a reference 173 | SUB-TC #1: PASS 174 | SUB-TC #2: Verify reading string from channel into a reference 175 | SUB-TC #2: PASS 176 | SUB-TC #3: Verify reading array from channel into a reference 177 | SUB-TC #3: PASS 178 | SUB-TC #4: Verify select write different data types into channel 179 | #0 var written : 180 | 0 181 | #1 var written : 182 | 100 183 | #2 var written : 184 | 'string' 185 | #3 var written : 186 | array ( 187 | ) 188 | #4 var written : 189 | array ( 190 | 'key' => 1, 191 | ) 192 | #5 var written : 193 | A::__set_state(array( 194 | 'a' => NULL, 195 | )) 196 | i am A 197 | #6 var written : 198 | NULL 199 | #7 var written : 200 | NULL 201 | #8 var written : 202 | Closure::__set_state(array( 203 | )) 204 | i am lamda 205 | 206 | 207 | -------------------------------------------------------------------------------- /tests/041_go_select_read_write_default.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select read write default 3 | 4 | --FILE-- 5 | 10]); 14 | 15 | 16 | go(function() use($ch){ 17 | $seq = 1; 18 | 19 | subtc(1); 20 | $read = 0; $write = 0; $df = 0; $v=1111; 21 | $ch->push(1); 22 | select( 23 | [ 24 | 'case', $ch, "->", &$v, function($v) use(&$read){ 25 | if($v===1) 26 | $read = 1; 27 | } 28 | ], 29 | [ 30 | 'default', function() use(&$df){ 31 | $df = 1; 32 | } 33 | ] 34 | ); 35 | assert($read===1 && $df===0); 36 | echo "success\n"; 37 | 38 | subtc(2); 39 | $read = 0; $write = 0; $df = 0; $v=1111; 40 | 41 | select( 42 | [ 43 | 'case', $ch, "<-", $v, function($v) use(&$write){ 44 | if($v==1111) 45 | $write = 1; 46 | } 47 | ], 48 | [ 49 | 'default', function() use(&$df){ 50 | echo "go default\n"; 51 | $df = 1; 52 | } 53 | ] 54 | ); 55 | $ch->pop(); 56 | assert($write===1 && $df===0); 57 | echo "success\n"; 58 | 59 | subtc(3); 60 | $read = 0; $write = 0; $df = 0; $v=1111; 61 | for($i=0; $i<10;$i++){ 62 | $ch->push($i); 63 | } 64 | 65 | select( 66 | [ 67 | 'case', $ch, "<-", &$v, function($v) use(&$write){ 68 | //if($v==1111) 69 | $write = 1; 70 | }, 71 | ], 72 | [ 73 | 'default', function() use(&$df){ 74 | $df = 1; 75 | } 76 | ] 77 | ); 78 | assert($write===0 && $df===1); 79 | echo "success\n"; 80 | 81 | subtc(4); 82 | $read = 0; $write = 0; $df = 0; $v=1111; 83 | for($i=0; $i<10;$i++){ 84 | $ch->pop(); 85 | } 86 | select( 87 | [ 88 | 'case', $ch, "->", &$v, function($v) use(&$read){ 89 | //if($v==1111) 90 | $read = 1; 91 | } 92 | ], 93 | [ 'default', function() use(&$df){ 94 | $df = 1; 95 | } 96 | ] 97 | ); 98 | assert($read===0 && $df===1); 99 | echo "success\n"; 100 | 101 | }); 102 | Scheduler::join(); 103 | 104 | ?> 105 | --EXPECT-- 106 | SUB-TC: #1 107 | success 108 | SUB-TC: #2 109 | success 110 | SUB-TC: #3 111 | success 112 | SUB-TC: #4 113 | success 114 | 115 | -------------------------------------------------------------------------------- /tests/042_go_select_read_write.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select read write 3 | 4 | --FILE-- 5 | 1000]); 15 | 16 | 17 | go(function() use($ch){ 18 | for($i=0;$i<500;$i++){ 19 | $ch->push($i); 20 | } 21 | $v = -1; $u = $v; $fail = false; 22 | while(true){ 23 | select( 24 | ['case',$ch, "->", /*$v,*/ function($data) use($i, &$read){ 25 | $read++; 26 | //echo "pass $i: read from chan: $data\n"; 27 | }], 28 | ['case',$ch, "<-", $i, function($data) use($i, &$write){ 29 | $write++; 30 | //echo "pass $i: written to chan: $data\n"; 31 | }] 32 | /*, 33 | _default(function(){ 34 | echo "this is default\n"; 35 | sleep(1); 36 | })*/ 37 | ); 38 | if( abs($write-$read) > 60 ) { 39 | echo "randomness check failed: diff: " .($write-$read) .PHP_EOL; 40 | $fail = true; 41 | } 42 | $i++; 43 | if($i==1000) break; 44 | usleep(100); 45 | } 46 | if($fail) echo "failed\n"; 47 | else echo "success\n"; 48 | }); 49 | 50 | Scheduler::join(); 51 | 52 | ?> 53 | --EXPECT-- 54 | SUB-TC: #1 55 | success 56 | -------------------------------------------------------------------------------- /tests/043_go_select_read_write_default_timer.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select read write default timer 3 | 4 | --FILE-- 5 | ", &$dummy, function($v) use($done, $begin){ 23 | //assert( $v===1 ); 24 | //var_dump($v); 25 | assert( time()-$begin == 1 ); 26 | 27 | $done->push("done"); 28 | }] 29 | ); 30 | 31 | $ret = $sel->loop($done); 32 | if(assert($ret=="done")){ 33 | echo "success\n"; 34 | } 35 | 36 | }); 37 | 38 | Scheduler::join(); 39 | 40 | subtc(2); 41 | $done = new Chan(1); 42 | 43 | go(function() use($done){ 44 | 45 | $begin = microtime(true); $i=0; 46 | 47 | //echo $begin; 48 | $dummy = 0; 49 | $sel = select( 50 | [ 'case', Time::tick(100*Time::MILLISECOND), /*"->", &$dummy, */ function($v) use($done, &$begin, &$i){ 51 | //assert( $v===1 ); 52 | assert( ($diff = abs( microtime(true)-$begin-0.1 )) < 0.05 ); 53 | //echo $diff . " "; 54 | $begin = microtime(true); 55 | 56 | $i++; 57 | }], 58 | ['default', function(){ 59 | usleep(1*1000); 60 | }] 61 | ); 62 | 63 | while($i<10){ 64 | $sel = $sel->select(); 65 | } 66 | 67 | echo "test completed\n"; 68 | 69 | //exit; 70 | 71 | }); 72 | 73 | Scheduler::join(); 74 | 75 | ?> 76 | --EXPECT-- 77 | SUB-TC: #1 78 | success 79 | SUB-TC: #2 80 | test completed 81 | 82 | 83 | -------------------------------------------------------------------------------- /tests/044_go_select_short_cut_syntax.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select shortcut syntax 3 | 4 | --FILE-- 5 | 10]); 14 | 15 | //error_reporting(0); 16 | 17 | go(function() use($ch){ 18 | $seq = 1; 19 | 20 | subtc(1); // unexpected extra parameter 21 | $read = 0; $write = 0; $df = 0; $v=1111; 22 | $ch->push(1); 23 | 24 | ob_start(); 25 | select( 26 | [ 27 | 'case', $ch, '->', &$v, function($v) use(&$read){ 28 | if($v===1) 29 | $read = 1; 30 | } 31 | ], 32 | [ 33 | 'default', function() use(&$df){ 34 | $df = 1; 35 | }, 'unexpected extra parameter' 36 | ] 37 | ); 38 | $res = ob_get_clean(); 39 | if( !preg_match("/.*case 2: unexpected extra parameter detected after the callable.*/s", $res) ){ 40 | echo "verify unexpected extra parameter can be detected: failed\n"; 41 | } 42 | 43 | assert($read===1 && $df===0); 44 | echo "success\n"; 45 | 46 | subtc(2); //receiving variable ommited 47 | $read = 0; $write = 0; $df = 0; $v=1111; 48 | $ch->push(1); 49 | 50 | select( 51 | [ 52 | 'case', $ch, '->', function($v) use(&$read){ 53 | if($v===1) 54 | $read = 1; 55 | } 56 | ], 57 | [ 58 | 'default', function() use(&$df){ 59 | $df = 1; 60 | } 61 | ] 62 | ); 63 | 64 | assert($read===1 && $df===0); 65 | echo "success\n"; 66 | 67 | subtc(3); //operator and receiving variable ommited 68 | $read = 0; $write = 0; $df = 0; $v=1111; 69 | $ch->push(1); 70 | 71 | select( 72 | [ 73 | 'case', $ch, function($v) use(&$read){ 74 | if($v===1) 75 | $read = 1; 76 | } 77 | ], 78 | [ 79 | 'default', function() use(&$df){ 80 | $df = 1; 81 | } 82 | ] 83 | ); 84 | 85 | assert($read===1 && $df===0); 86 | echo "success\n"; 87 | 88 | }); 89 | 90 | 91 | Scheduler::join(); 92 | 93 | ?> 94 | --EXPECT-- 95 | SUB-TC: #1 96 | success 97 | SUB-TC: #2 98 | success 99 | SUB-TC: #3 100 | success 101 | 102 | 103 | -------------------------------------------------------------------------------- /tests/045_go_select_error_invalid_case_type.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select invalid case type 3 | 4 | --FILE-- 5 | 10]); 14 | 15 | //error_reporting(0); 16 | 17 | go(function() use($ch){ 18 | $seq = 1; 19 | 20 | subtc(1); // unexpected extra parameter 21 | $read = 0; $write = 0; $df = 0; $v=1111; 22 | $ch->push(1); 23 | 24 | select( 25 | [ 26 | 'invalid', $ch, '->', &$v, function($v) use(&$read){ 27 | if($v===1) 28 | $read = 1; 29 | } 30 | ], 31 | [ 32 | 'default', function() use(&$df){ 33 | $df = 1; 34 | }, 'unexpected extra parameter' 35 | ] 36 | ); 37 | 38 | }); 39 | 40 | 41 | Scheduler::join(); 42 | 43 | ?> 44 | --EXPECTREGEX-- 45 | .*invalid case type invalid.* 46 | -------------------------------------------------------------------------------- /tests/046_go_select_error_invalid_channel_parameter.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select error invalid channel parameter 3 | 4 | --FILE-- 5 | 10]); 14 | 15 | //error_reporting(0); 16 | 17 | go(function() use($ch){ 18 | $seq = 1; 19 | 20 | subtc(1); // unexpected extra parameter 21 | $read = 0; $write = 0; $df = 0; $v=1111; 22 | $ch->push(1); 23 | 24 | select( 25 | [ 26 | 'case', $v, '->', &$v, function($v) use(&$read){ 27 | if($v===1) 28 | $read = 1; 29 | } 30 | ], 31 | [ 32 | 'default', function() use(&$df){ 33 | $df = 1; 34 | }, 'unexpected extra parameter' 35 | ] 36 | ); 37 | 38 | }); 39 | 40 | 41 | Scheduler::join(); 42 | 43 | ?> 44 | --EXPECTREGEX-- 45 | .*phpgo: select\(\): case 1: invalid parameter type, object of go\\Chan expected.* 46 | -------------------------------------------------------------------------------- /tests/047_go_select_error_insufficient_parameters.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select error insuffient parameters 3 | 4 | --FILE-- 5 | 10]); 14 | 15 | //error_reporting(0); 16 | 17 | go(function() use($ch){ 18 | $seq = 1; 19 | 20 | subtc(1); // unexpected extra parameter 21 | $read = 0; $write = 0; $df = 0; $v=1111; 22 | $ch->push(1); 23 | 24 | select( 25 | [ 26 | 'case', $ch, '->', &$v, /*function($v) use(&$read){ 27 | if($v===1) 28 | $read = 1; 29 | }*/ 30 | ], 31 | [ 32 | 'default', function() use(&$df){ 33 | $df = 1; 34 | }, 'unexpected extra parameter' 35 | ] 36 | ); 37 | 38 | }); 39 | 40 | 41 | Scheduler::join(); 42 | 43 | ?> 44 | --EXPECTREGEX-- 45 | .*phpgo: select\(\): case 1: insufficient parameter count.* 46 | -------------------------------------------------------------------------------- /tests/048_go_select_error_insufficient_parameters_2.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go select error insuffient parameters - 2 3 | 4 | --FILE-- 5 | 10]); 14 | 15 | //error_reporting(0); 16 | 17 | go(function() use($ch){ 18 | $seq = 1; 19 | 20 | subtc(1); // unexpected extra parameter 21 | $read = 0; $write = 0; $df = 0; $v=1111; 22 | $ch->push(1); 23 | 24 | select( 25 | [ 26 | 'case', $ch, '->', &$v,function($v) use(&$read){ 27 | if($v===1) 28 | $read = 1; 29 | } 30 | ], 31 | [ 32 | 'default', /*function() use(&$df){ 33 | $df = 1; 34 | }, 'unexpected extra parameter' */ 35 | ] 36 | ); 37 | 38 | }); 39 | 40 | 41 | Scheduler::join(); 42 | 43 | ?> 44 | --EXPECTREGEX-- 45 | .*phpgo: select\(\): case 2: insufficient parameter count.* 46 | -------------------------------------------------------------------------------- /tests/050_go_redis_con_shared_by_muti_go_routines.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go redis connection shared by mutilple go routines (deemed to be FAIL) 3 | 4 | --SKIPIF-- 5 | connect($arr[0], $arr[1]); 17 | $r->set("___test__redis_availability__", 1); 18 | if( $r->get("___test__redis_availability__") != 1 ) 19 | echo "skip\n"; 20 | else{ 21 | $r->del("___test__redis_availability__"); 22 | } 23 | } 24 | ?> 25 | --FILE-- 26 | 1000]); 36 | 37 | $redis = new \Redis(); 38 | $redis->connect("127.0.0.1", "6379"); 39 | 40 | go(function() use($redis){ 41 | for($i=0; $i<10000; $i++){ 42 | $redis->set("foo", $i); 43 | //echo "foo: " . $redis->get("foo") . PHP_EOL; 44 | assert( ($r = $redis->get("foo")) == $i); 45 | 46 | if($r != $i) break; 47 | } 48 | if($i==10000) 49 | echo "get set of foo: success\n"; 50 | }); 51 | 52 | //$redis = new \Redis(); 53 | //$redis->connect("127.0.0.1", "6379"); 54 | 55 | go(function() use($redis){ 56 | for($i=0; $i<10000; $i++){ 57 | $redis->set("bar", $i); 58 | assert( ($r = $redis->get("bar")) == $i); 59 | 60 | if($r != $i) break; 61 | } 62 | if($i==10000) 63 | echo "get set of bar: success\n"; 64 | }); 65 | 66 | Scheduler::join(); 67 | 68 | ?> 69 | --EXPECT-- 70 | SUB-TC: #1 71 | get set of foo: success 72 | get set of bar: success 73 | -------------------------------------------------------------------------------- /tests/051_go_redis_dedicate_cons_by_muti_go_routines.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go redis dedicated connection by mutilple go routines 3 | 4 | --SKIPIF-- 5 | connect($arr[0], $arr[1]); 14 | $r->set("___test__redis_availability__", 1); 15 | if( $r->get("___test__redis_availability__") != 1 ) 16 | echo "skip\n"; 17 | else{ 18 | $r->del("___test__redis_availability__"); 19 | } 20 | } 21 | ?> 22 | --FILE-- 23 | 1000]); 41 | 42 | $redis = new \Redis(); 43 | $redis->connect($host, $port); 44 | 45 | go(function() use($redis){ 46 | for($i=0; $i<10000; $i++){ 47 | $redis->set("foo", $i); 48 | 49 | $r = $redis->get("foo"); 50 | //echo "foo: " . $redis->get("foo") . PHP_EOL; 51 | assert( $r == $i); 52 | 53 | if($r != $i) break; 54 | } 55 | if($i==10000) 56 | echo "get set of foo: success\n"; 57 | }); 58 | 59 | $redis = new \Redis(); 60 | $redis->connect($host, $port); 61 | 62 | go(function() use($redis){ 63 | for($i=0; $i<10000; $i++){ 64 | $redis->set("bar", $i); 65 | $r = $redis->get("bar"); 66 | assert( $r == $i); 67 | 68 | if($r != $i) break; 69 | } 70 | if($i==10000) 71 | echo "get set of bar: success\n"; 72 | }); 73 | 74 | Scheduler::join(); 75 | 76 | ?> 77 | --EXPECT-- 78 | SUB-TC: #1 79 | get set of foo: success 80 | get set of bar: success 81 | -------------------------------------------------------------------------------- /tests/060_go_mysql_con_shared_by_muti_go_routines.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go PDO connection shared by mutilple go routines (deemed to be FAIL) 3 | 4 | --SKIPIF-- 5 | query("select * from test;"); 18 | if(empty($rs)) 19 | echo "skip\n"; 20 | 21 | } 22 | ?> 23 | --FILE-- 24 | 1000]); 34 | 35 | $user='root'; 36 | $pass='Baipeng2016'; 37 | $dsn="mysql:host=10.116.71.188;dbname=test"; 38 | 39 | $db = new PDO($dsn, $user, $pass); 40 | $succ1 = false; 41 | go(function() use($db, &$succ1){ 42 | for($i=0; $i<1000; $i++){ 43 | $db->query("update test set str='$i' where id=1;"); 44 | $r = $db->query("select * from test where id=1;"); 45 | 46 | $rs = array(); 47 | foreach($r as $row){ 48 | $rs[] = $row; 49 | } 50 | 51 | if ( !assert($rs[0]['id'] == 1 and $rs[0]['str'] == $i) ) { 52 | break; 53 | }; 54 | } 55 | if($i==1000) 56 | $succ1 = true; 57 | }); 58 | 59 | //$db = new PDO($dsn, $user, $pass); 60 | $succ2 = false; 61 | go(function() use($db, &$succ2){ 62 | for($i=0; $i<1000; $i++){ 63 | $db->query("update test set str='$i' where id=2;"); 64 | $r = $db->query("select * from test where id=2;"); 65 | 66 | $rs = array(); 67 | foreach($r as $row){ 68 | $rs[] = $row; 69 | } 70 | 71 | if ( !assert($rs[0]['id'] == 2 and $rs[0]['str'] == $i) ) { 72 | break; 73 | }; 74 | } 75 | if($i==1000) 76 | $succ2 = true; 77 | }); 78 | 79 | Scheduler::join(); 80 | 81 | if($succ1 && $succ2) 82 | echo "success\n"; 83 | 84 | ?> 85 | --EXPECT-- 86 | SUB-TC: #1 87 | success 88 | -------------------------------------------------------------------------------- /tests/061_go_mysql_dedicate_cons_by_muti_go_routines.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go mysql dedicated PDO connection by mutilple go routines 3 | 4 | --SKIPIF-- 5 | query("select * from test;"); 16 | if(empty($rs)) 17 | echo "skip\n"; 18 | 19 | } 20 | ?> 21 | --FILE-- 22 | 1000]); 32 | 33 | $user='root'; 34 | $pass='Baipeng2016'; 35 | $dsn="mysql:host=10.116.71.188;dbname=test"; 36 | 37 | $db = new PDO($dsn, $user, $pass); 38 | 39 | $db->exec("delete from test where id = 1 or id = 2" ); 40 | $db->exec("insert into test(id, str) values(1, 'aaa') " ); 41 | $db->exec("insert into test(id, str) values(2, 'bbb') " ); 42 | 43 | $succ1 = false; 44 | go(function() use($db, &$succ1){ 45 | for($i=0; $i<1000; $i++){ 46 | $db->query("update test set str='$i' where id=1;"); 47 | $r = $db->query("select * from test where id=1;"); 48 | 49 | $rs = array(); 50 | foreach($r as $row){ 51 | $rs[] = $row; 52 | } 53 | 54 | if ( !assert($rs[0]['id'] == 1 and $rs[0]['str'] == $i) ) { 55 | break; 56 | }; 57 | } 58 | if($i==1000) 59 | $succ1 = true; 60 | }); 61 | 62 | $db = new PDO($dsn, $user, $pass); 63 | $succ2 = false; 64 | go(function() use($db, &$succ2){ 65 | for($i=0; $i<1000; $i++){ 66 | $db->query("update test set str='$i' where id=2;"); 67 | $r = $db->query("select * from test where id=2;"); 68 | 69 | $rs = array(); 70 | foreach($r as $row){ 71 | $rs[] = $row; 72 | } 73 | 74 | if ( !assert($rs[0]['id'] == 2 and $rs[0]['str'] == $i) ) { 75 | break; 76 | }; 77 | } 78 | if($i==1000) 79 | $succ2 = true; 80 | }); 81 | 82 | Scheduler::join(); 83 | 84 | $db->exec("delete from test where id = 1 or id = 2" ); 85 | 86 | if($succ1 && $succ2) 87 | echo "success\n"; 88 | 89 | ?> 90 | --EXPECT-- 91 | SUB-TC: #1 92 | success 93 | -------------------------------------------------------------------------------- /tests/071_go_waitgroup_test.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Waitgroup test 3 | 4 | --FILE-- 5 | wait(); 20 | echo "go 0: wait done\n"; 21 | }); 22 | 23 | echo "add 10 to wg\n"; 24 | $wg->add(10); 25 | 26 | for($i=0;$i<10;$i++){ 27 | go(function() use($wg, $i){ 28 | echo "go $i: done\n"; 29 | $wg->done(); 30 | }); 31 | } 32 | Scheduler::join(); 33 | 34 | 35 | subtc(2, "verify wg wait outside a go routine"); 36 | 37 | $wg = new Waitgroup(); 38 | assert(!empty($wg)); 39 | 40 | echo "add 10 to wg\n"; 41 | $wg->add(10); 42 | 43 | for($i=0;$i<10;$i++){ 44 | go(function() use($wg, $i){ 45 | echo "go $i: done\n"; 46 | $wg->done(); 47 | }); 48 | } 49 | echo "wait outside go\n"; 50 | $wg->wait(); 51 | echo "wait outside go: done\n" 52 | //Scheduler::join(); 53 | 54 | ?> 55 | --EXPECT-- 56 | SUB-TC: #1 - verify wg wait in a go routine 57 | add 10 to wg 58 | go 0: wait 59 | go 0: done 60 | go 1: done 61 | go 2: done 62 | go 3: done 63 | go 4: done 64 | go 5: done 65 | go 6: done 66 | go 7: done 67 | go 8: done 68 | go 9: done 69 | go 0: wait done 70 | SUB-TC: #2 - verify wg wait outside a go routine 71 | add 10 to wg 72 | wait outside go 73 | go 0: done 74 | go 1: done 75 | go 2: done 76 | go 3: done 77 | go 4: done 78 | go 5: done 79 | go 6: done 80 | go 7: done 81 | go 8: done 82 | go 9: done 83 | wait outside go: done 84 | -------------------------------------------------------------------------------- /tests/081_go_mutex_basic_test.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Mutex basic test 3 | 4 | --FILE-- 5 | lock(); 37 | echo "go 1 obtained lock\n"; 38 | }); 39 | 40 | go(function() use($m){ 41 | echo "go 2 try to obtain lock\n"; 42 | $m->lock(); 43 | echo "go 2 obtained lock\n"; 44 | }); 45 | 46 | go(function() use($m){ 47 | usleep(1000); 48 | echo "go 3 release lock\n"; 49 | $m->unlock(); 50 | }); 51 | 52 | 53 | Scheduler::join(); 54 | 55 | subtc(2, "Mutex created not signaled, lock unlock test" ); 56 | $m1 = new Mutex(false); 57 | my_assert(2,!empty($m1)); 58 | 59 | go(function() use($m1){ 60 | echo "go 3 try to obtain lock\n"; 61 | $m1->lock(); 62 | echo "go 3 obtained lock\n"; 63 | echo "go 3 release lock\n"; 64 | $m1->unlock(); 65 | }); 66 | 67 | go(function() use($m1){ 68 | echo "go 4 try to obtain lock\n"; 69 | $m1->lock(); 70 | echo "go 4 obtained lock\n"; 71 | echo "go 4 release lock\n"; 72 | $m1->unlock(); 73 | }); 74 | 75 | go(function() use($m1){ 76 | usleep(1000); 77 | echo "go 5 release lock\n"; 78 | $m1->unlock(); 79 | }); 80 | 81 | Scheduler::join(); 82 | 83 | 84 | subtc(3, "Mutex tryLock/isLock test"); 85 | $m1 = new Mutex(false); 86 | my_assert(3,!empty($m1)); 87 | my_assert(3, $m1->isLock()); 88 | 89 | go(function() use($m1){ 90 | $i = 0; 91 | while( !$m1->tryLock() ){ 92 | ++$i; 93 | my_assert(3, $m1->isLock()); 94 | Time::sleep(100*Time::MILLISECOND); 95 | } 96 | my_assert(3, $i == 10); 97 | my_assert(3, $m1->isLock()); 98 | $m1->unlock(); 99 | my_assert(3, !$m1->isLock()); 100 | }); 101 | 102 | go(function() use($m1){ 103 | Time::sleep(1*Time::SECOND); 104 | $m1->unlock(); 105 | my_assert(3, !$m1->isLock()); 106 | }); 107 | 108 | Scheduler::join(); 109 | passtc(3,true); 110 | 111 | ?> 112 | --EXPECT-- 113 | SUB-TC #1: Mutex created signaled, lock unlock test 114 | go 1 try to obtain lock 115 | go 1 obtained lock 116 | go 2 try to obtain lock 117 | go 3 release lock 118 | go 2 obtained lock 119 | SUB-TC #2: Mutex created not signaled, lock unlock test 120 | go 3 try to obtain lock 121 | go 4 try to obtain lock 122 | go 5 release lock 123 | go 3 obtained lock 124 | go 3 release lock 125 | go 4 obtained lock 126 | go 4 release lock 127 | SUB-TC #3: Mutex tryLock/isLock test 128 | SUB-TC #3: PASS 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /tests/082_go_mutex_recursively_lock.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go Mutex recursively lock 3 | 4 | --FILE-- 5 | lock(); 22 | echo "go 1 obtained lock\n"; 23 | echo "..go 1 try to obtain lock: 2nd time\n"; 24 | $m->lock(); 25 | echo "..go 1 obtained lock: 2nd time\n"; 26 | echo "....go 1 try to obtain lock: 3rd time\n"; 27 | $m->lock(); 28 | echo "....go 1 obtained lock: 3rd time\n"; 29 | 30 | 31 | echo "....go 1 release lock\n"; 32 | $m->unlock(); 33 | echo "..go 1 release lock: 2nd time\n"; 34 | $m->unlock(); 35 | echo "go 1 release lock: 3rd time\n"; 36 | $m->unlock(); 37 | }); 38 | 39 | go(function() use($m){ 40 | echo "go 2 try to obtain lock\n"; 41 | $m->lock(); 42 | echo "go 2 obtained lock\n"; 43 | }); 44 | 45 | Scheduler::join(); 46 | 47 | ?> 48 | --EXPECT-- 49 | SUB-TC: #1 50 | create mutex, signaled 51 | go 1 try to obtain lock 52 | go 1 obtained lock 53 | ..go 1 try to obtain lock: 2nd time 54 | ..go 1 obtained lock: 2nd time 55 | ....go 1 try to obtain lock: 3rd time 56 | ....go 1 obtained lock: 3rd time 57 | ....go 1 release lock 58 | ..go 1 release lock: 2nd time 59 | go 1 release lock: 3rd time 60 | go 2 try to obtain lock 61 | go 2 obtained lock 62 | 63 | -------------------------------------------------------------------------------- /tests/091_go_runtime_basic_test.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go runtime basic test 3 | 4 | --FILE-- 5 | 47 | --EXPECT-- 48 | SUB-TC: #1 49 | go 1: numGoroutine: 4 50 | go 4 51 | go 3 52 | numGoroutine:1 53 | go 2: numGoroutine: 1 54 | numGoroutine:0 55 | 56 | -------------------------------------------------------------------------------- /tests/101_go_routines_with_swoole_server.phpt__: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go routines with swoole server 3 | 4 | --SKIPIF-- 5 | 6 | 7 | --FILE-- 8 | set(array( 20 | 'worker_num' => 8, 21 | 'max_request' => 10000, 22 | 'max_conn' => 65535, 23 | 'dispatch_mode' => 2, 24 | 'debug_mode'=> 0, 25 | 'daemonize' => false, 26 | )); 27 | 28 | $http->on('request', function ($request, $response) { 29 | go(function() use($request, $response){ 30 | $response->end("

Hello Swoole. #".rand(1000, 9999)."

\n"); 31 | }); 32 | 33 | //Scheduler::run(); 34 | }); 35 | 36 | $http->on('WorkerStart', function($serv, $worker_id) { 37 | 38 | if ($worker_id < $serv->setting['worker_num']) { 39 | //worker 40 | 41 | // set a 10ms timer who does nothing, 42 | // just to ensure the Swoole\Event::cycle be invoked at least every 10ms 43 | 44 | swoole_timer_tick( 45 | 10, 46 | function($timer_id, $param){ 47 | //Scheduler::run(); 48 | }, 49 | "param" 50 | ); 51 | 52 | Event::cycle( function () { 53 | $pass = 0; 54 | while( ($run = Scheduler::run()) && $pass++ < 200 ); 55 | }); 56 | 57 | } else { 58 | //task worker 59 | } 60 | }); 61 | 62 | /* 63 | swoole_timer_tick( 64 | 10, 65 | function($timer_id, $param){ 66 | Scheduler::run(); 67 | }, 68 | "param" 69 | );*/ 70 | 71 | $http->start(); 72 | 73 | 74 | ?> 75 | --EXPECT-- 76 | SUB-TC: #1 77 | 78 | 79 | -------------------------------------------------------------------------------- /tests/102_go_routines_with_swoole_server_nogo.phpt__: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go routines with swoole server 3 | 4 | --SKIPIF-- 5 | 6 | 7 | --FILE-- 8 | set(array( 20 | 'worker_num' => 8, 21 | 'max_request' => 10000, 22 | 'max_conn' => 65535, 23 | 'dispatch_mode' => 2, 24 | 'debug_mode'=> 0, 25 | 'daemonize' => false, 26 | )); 27 | 28 | $http->on('request', function ($request, $response) { 29 | //go(function() use($request, $response){ 30 | $response->end("

Hello Swoole. #".rand(1000, 9999)."

\n"); 31 | //}); 32 | 33 | //Scheduler::run(); 34 | }); 35 | 36 | $http->on('WorkerStart', function($serv, $worker_id) { 37 | 38 | if ($worker_id < $serv->setting['worker_num']) { 39 | //worker 40 | 41 | // set a 10ms timer who does nothing, 42 | // just to ensure the Swoole\Event::cycle be invoked at least every 10ms 43 | 44 | swoole_timer_tick( 45 | 10, 46 | function($timer_id, $param){ 47 | //Scheduler::run(); 48 | }, 49 | "param" 50 | ); 51 | 52 | Event::cycle( function () { 53 | $pass = 0; 54 | while( ($run = Scheduler::run()) && $pass++ < 200 ); 55 | }); 56 | 57 | } else { 58 | //task worker 59 | } 60 | }); 61 | 62 | /* 63 | swoole_timer_tick( 64 | 10, 65 | function($timer_id, $param){ 66 | Scheduler::run(); 67 | }, 68 | "param" 69 | );*/ 70 | 71 | $http->start(); 72 | 73 | 74 | ?> 75 | --EXPECT-- 76 | SUB-TC: #1 77 | 78 | 79 | -------------------------------------------------------------------------------- /tests/111_go_scheduler_run.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go scheduler run 3 | 4 | --SKIPIF-- 5 | 6 | 7 | --FILE-- 8 | 0) 47 | echo "^run $run tasks\n"; 48 | 49 | if(Runtime::numGoroutine() ===0 ) 50 | break; 51 | } 52 | 53 | subtc(2); 54 | 55 | go(function(){ 56 | echo "go 1\n"; 57 | }); 58 | go(function(){ 59 | usleep(100*1000); 60 | echo "go 2\n"; 61 | }); 62 | 63 | Scheduler::join(1); 64 | echo "after join(1)\n"; 65 | Scheduler::join(0); 66 | 67 | subtc(3); 68 | 69 | go(function(){ 70 | echo "go 1\n"; 71 | }); 72 | go(function(){ 73 | usleep(100*1000); 74 | echo "go 2\n"; 75 | }); 76 | go(function(){ 77 | usleep(200*1000); 78 | echo "go 3\n"; 79 | Runtime::quit(1); 80 | }); 81 | 82 | Scheduler::loop(); 83 | 84 | ?> 85 | --EXPECT-- 86 | SUB-TC: #1 87 | return from go 1 88 | go 2 89 | ^run 2 tasks 90 | go 3 91 | ^run 1 tasks 92 | go 4 93 | ^run 1 tasks 94 | return from go 2 95 | ^run 1 tasks 96 | return from go 3 97 | ^run 1 tasks 98 | return from go 4 99 | ^run 1 tasks 100 | SUB-TC: #2 101 | go 1 102 | after join(1) 103 | go 2 104 | SUB-TC: #3 105 | go 1 106 | go 2 107 | go 3 108 | 109 | 110 | -------------------------------------------------------------------------------- /tests/112_go_scheduler_goid.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go goid 3 | 4 | --SKIPIF-- 5 | 6 | 7 | --FILE-- 8 | 36 | --EXPECT-- 37 | SUB-TC: #1 38 | success 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/131_go_time_basic_test.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go time basic test 3 | 4 | --FILE-- 5 | 1.1 || $t1 - $t < 0.9 ){ 30 | echo "should sleep 1 second but slept " 31 | . ( $t1-$t ) . " seconds\n"; 32 | }else{ 33 | echo "pass\n"; 34 | } 35 | 36 | subtc(3); 37 | go(function (){ //go 1 38 | $chan = Time::After(1*Time::SECOND); //go 2 39 | 40 | go(function () use($chan){ //go 3 41 | $time0 = time(); $mtime0 = microtime(true); 42 | while( true ){ 43 | $v = $chan->tryPop() ; 44 | if( !empty($v) ) break; 45 | Time::sleep(20 * Time::MILLISECOND); // = usleep(20 * 1000); 46 | 47 | if( microtime(true)-$mtime0 > 1.05 ) { 48 | echo "timer should expire at 1 second but did not expire until " 49 | . ( microtime(true)-$mtime0 ) . " seconds\n"; 50 | return; 51 | } 52 | } 53 | if( abs(microtime(true) - $v) > 0.05 ){ 54 | echo "timer should expire at ". microtime(true) . " but expired at " . $v . PHP_EOL; 55 | return; 56 | } 57 | if( microtime(true) - $mtime0 > 1.05 ){ 58 | echo "timer should expire at ". $mtime0 . "+1 second but expired at " 59 | . microtime(true) . PHP_EOL; 60 | return; 61 | } 62 | echo "verify Time::After : 1 second : pass\n"; 63 | }); 64 | }); 65 | 66 | Scheduler::join(); 67 | 68 | subtc(4); 69 | go(function (){ //go 1 70 | $c = 2000; 71 | for($i=0;$i<$c; $i++){ 72 | $ch[$i] = Time::After($i*Time::MILLISECOND); 73 | } 74 | 75 | for($i=0;$i<$c; $i++){ 76 | if( empty($ch[$i]) ){ 77 | echo "some timers were failed to start\n"; 78 | return; 79 | } 80 | } 81 | 82 | go( function() use($ch, $c){ 83 | $count = 0; 84 | $time0 = time(); 85 | while($count<$c){ 86 | for($i=0; $i<$c; $i++){ 87 | $p = $ch[$i]->TryPop(); 88 | if(!empty($p)){ 89 | $count ++; 90 | //echo "ch $i returns $p\n"; 91 | } 92 | } 93 | 94 | Time::sleep(10 * Time::MILLISECOND); // = usleep(10*1000); 95 | 96 | if( time()-$time0 > ($c+1000) / 1000 ) { 97 | echo "timer should expire at ". ($c/1000) . 98 | " seconds but expired at " . (time()-$time0) . 99 | " seconds\n"; 100 | return; 101 | } 102 | } 103 | echo "verify start of 2000 timers : pass\n"; 104 | }); 105 | }); 106 | 107 | Scheduler::join(); 108 | 109 | subtc(5); 110 | go(function (){ //go 1 111 | 112 | $ch = Time::tick(100*Time::MILLISECOND); 113 | $done = new Chan(1); 114 | 115 | go( function() use($ch, $done){ 116 | select( 117 | ['case', $ch, function($v) use($done){ 118 | static $t0 = 0; 119 | $t1 = microtime(true); 120 | if($t0){ 121 | if( $t1-$t0 > 0.12 || $t1 - $t0 < 0.08 ){ 122 | echo "timer interval expected to be 0.1 seconds, ". ($t1-$t0) ." in actual\n"; 123 | } 124 | } 125 | $t0 = $t1; 126 | //echo microtime(true) . ":". $v . PHP_EOL; 127 | 128 | static $i = 0; 129 | if($i++ >= 10) { 130 | $done->close(); 131 | } 132 | }] 133 | )->loop($done); 134 | 135 | echo "verify Timer::tick(): pass\n"; 136 | }); 137 | }); 138 | 139 | Scheduler::join(); 140 | 141 | ?> 142 | --EXPECT-- 143 | SUB-TC: #1 144 | 1 145 | 1000 146 | 1000000 147 | 1000000000 148 | 60000000000 149 | 3600000000000 150 | SUB-TC: #2 151 | verify sleep of 1 second : outside of go routine: pass 152 | SUB-TC: #3 153 | verify Time::After : 1 second : pass 154 | SUB-TC: #4 155 | verify start of 2000 timers : pass 156 | SUB-TC: #5 157 | verify Timer::tick(): pass 158 | -------------------------------------------------------------------------------- /tests/141_go_exit.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go exit test 3 | 4 | --FILE-- 5 | 67 | --EXPECTREGEX-- 68 | SUB-TC: #1.*can only terminate the affected go routine - success[\s]*SUB-TC: #2.*this should show -------------------------------------------------------------------------------- /tests/151_go_compatibility_file_get_content.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go compatibility test for file_get_contents 3 | 4 | --FILE-- 5 | 47 | --EXPECT-- 48 | success 49 | success 50 | -------------------------------------------------------------------------------- /tests/153_go_compatibility_curl.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go compatibility test for curl 3 | 4 | --FILE-- 5 | 93 | --EXPECT-- 94 | success 95 | success 96 | -------------------------------------------------------------------------------- /tests/161_go_shutdown_func.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go shutdown function test 3 | 4 | --FILE-- 5 | 35 | --EXPECT-- 36 | go1 returns 37 | this is shutdown function inside go1 38 | go2 returns 39 | this is shutdown function inside go2 40 | main script returns 41 | this is shutdown function outside a go 42 | -------------------------------------------------------------------------------- /tests/162_go_error_handler.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Go error handler test 3 | 4 | --FILE-- 5 | 51 | --EXPECT-- 52 | main script set error handler 53 | main script trggers error 54 | error handler in main: this is error from outside go, line 41 55 | go 1 trggers error 56 | error handler in main: this is error from inside go 1, line 12 57 | go 2 set error handler 58 | go 3 trggers error 59 | error handler in go 2: this is error from inside go 3, line 36 60 | go 1 set error handler 61 | go 2 trggers error 62 | error handler in go 1: this is error from inside go 2, line 29 63 | main script returns 64 | -------------------------------------------------------------------------------- /tests/known_issues/block_object_was_waiting_when_destructor.php: -------------------------------------------------------------------------------- 1 | pop() 12 | 13 | The main progrom got the popped data by creating an (invisible) child go routine and 14 | do channel pop in that child go routine 15 | 16 | the usleep() must be in place otherwise this error won't happen: because if the usleep is not 17 | in place, the main program's child go routining won't have chance to run, so it's the go routine 18 | created by go() that get data pushed and then poped, and so the main program won't get data 19 | and won't end, and so no error reported 20 | */ 21 | 22 | $ch = new Chan(1); 23 | go(function($ch){ 24 | usleep(1); 25 | $ch->push(1); 26 | var_dump($ch->pop()); 27 | echo "return from go\n"; 28 | },[$ch]); 29 | 30 | $v = $ch->pop(); // wait the go routine to finish 31 | --------------------------------------------------------------------------------