Merge branch 'thread'

some libuv functions requires threading feature.
you have to re-compile your PHPwith --enable-maintainer-zts if you want to use threading functions.

NOTE: thread feature runs new php vm another thread. current implementation doesn't copy current
executor globals. so you have to load bootstrap file (like composer's autoloader) manually.
This commit is contained in:
Shuhei Tanuma 2014-02-24 14:37:55 +09:00
commit f0d8db93ed
5 changed files with 148 additions and 25 deletions

View File

@ -2129,9 +2129,12 @@ send async callback immidiately
##### *Example* ##### *Example*
### void uv_queue_work(resource $loop, callable $callback, callable $after_callback) ### void uv_queue_work(resource $loop, callable $callback, callable $after_callback)
##### *Description*
execute callbacks in another thread (requires Thread Safe enabled PHP)
### resource uv_fs_open(resource $loop, string $path, long $flag, long $mode, callable $callback) ### resource uv_fs_open(resource $loop, string $path, long $flag, long $mode, callable $callback)
@ -2835,8 +2838,7 @@ $poll = uv_poll_init(uv_default_loop(), $fd);
##### *Note* ##### *Note*
* if you want to use a socket. please use uv_poll_init_socket instead of this. Windows can't handle socket with this function. * if you want to use a socket. please use uv_poll_init_socket instead of this. Windows can't handle socket with this function.
* some platform doesn't support file descriptor on these method.
### uv uv_poll_init_socket([resource $uv_loop], zval fd) ### uv uv_poll_init_socket([resource $uv_loop], zval fd)

View File

@ -20,6 +20,7 @@
#include "php_uv.h" #include "php_uv.h"
#include "ext/standard/info.h" #include "ext/standard/info.h"
#ifndef PHP_UV_DEBUG #ifndef PHP_UV_DEBUG
#define PHP_UV_DEBUG 0 #define PHP_UV_DEBUG 0
#endif #endif
@ -168,6 +169,16 @@ zend_fcall_info empty_fcall_info = { 0, NULL, NULL, NULL, NULL, 0, NULL, NULL, 0
#define PHP_UV_DEBUG_RESOURCE_REFCOUNT(name, resource_id) #define PHP_UV_DEBUG_RESOURCE_REFCOUNT(name, resource_id)
#endif #endif
#ifdef ZTS
#define UV_FETCH_ALL(ls, id, type) ((type) (*((void ***) ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])
#define UV_FETCH_CTX(ls, id, type, element) (((type) (*((void ***) ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define UV_CG(ls, v) UV_FETCH_CTX(ls, compiler_globals_id, zend_compiler_globals*, v)
#define UV_CG_ALL(ls) UV_FETCH_ALL(ls, compiler_globals_id, zend_compiler_globals*)
#define UV_EG(ls, v) UV_FETCH_CTX(ls, executor_globals_id, zend_executor_globals*, v)
#define UV_SG(ls, v) UV_FETCH_CTX(ls, sapi_globals_id, sapi_globals_struct*, v)
#define UV_EG_ALL(ls) UV_FETCH_ALL(ls, executor_globals_id, zend_executor_globals*)
#endif
extern void php_uv_init(TSRMLS_D); extern void php_uv_init(TSRMLS_D);
@ -1197,11 +1208,10 @@ static int php_uv_do_callback(zval **retval_ptr, zval *callback, zval ***params,
return error; return error;
} }
static int php_uv_do_callback2(zval **retval_ptr, php_uv_t *uv, zval ***params, int param_count, enum php_uv_callback_type type TSRMLS_DC) static int php_uv_do_callback2(zval **retval_ptr, php_uv_t *uv, zval ***params, int param_count, enum php_uv_callback_type type TSRMLS_DC)
{ {
int error = 0; int error = 0;
if (ZEND_FCI_INITIALIZED(uv->callback[type]->fci)) { if (ZEND_FCI_INITIALIZED(uv->callback[type]->fci)) {
uv->callback[type]->fci.params = params; uv->callback[type]->fci.params = params;
uv->callback[type]->fci.retval_ptr_ptr = retval_ptr; uv->callback[type]->fci.retval_ptr_ptr = retval_ptr;
@ -1214,11 +1224,77 @@ static int php_uv_do_callback2(zval **retval_ptr, php_uv_t *uv, zval ***params,
} else { } else {
error = -2; error = -2;
} }
//zend_fcall_info_args_clear(&uv->callback[type]->fci, 0);
return error;
}
#ifdef ZTS
static int php_uv_do_callback3(zval **retval_ptr, php_uv_t *uv, zval ***params, int param_count, enum php_uv_callback_type type)
{
int error = 0;
zend_executor_globals *ZEG = NULL;
void ***tsrm_ls, ***old;
if (ZEND_FCI_INITIALIZED(uv->callback[type]->fci)) {
tsrm_ls = tsrm_new_interpreter_context();
old = tsrm_set_interpreter_context(tsrm_ls);
PG(expose_php) = 0;
PG(auto_globals_jit) = 0;
php_request_startup(TSRMLS_C);
ZEG = UV_EG_ALL(TSRMLS_C);
ZEG->in_execution = 1;
ZEG->current_execute_data=NULL;
ZEG->current_module=phpext_uv_ptr;
ZEG->This = NULL;
uv->callback[type]->fci.params = params;
uv->callback[type]->fci.retval_ptr_ptr = retval_ptr;
uv->callback[type]->fci.param_count = param_count;
uv->callback[type]->fci.no_separation = 1;
uv->callback[type]->fci.object_ptr = ZEG->This;
uv->callback[type]->fcc.initialized = 1;
uv->callback[type]->fcc.calling_scope = NULL;
uv->callback[type]->fcc.called_scope = NULL;
uv->callback[type]->fcc.object_ptr = ZEG->This;
if (zend_call_function(&uv->callback[type]->fci, &uv->callback[type]->fcc TSRMLS_CC) != SUCCESS) {
error = -1;
}
{
zend_op_array *ops = &uv->callback[type]->fcc.function_handler->op_array;
if (ops) {
if (ops->run_time_cache) {
efree(ops->run_time_cache);
ops->run_time_cache = NULL;
}
}
}
if (retval_ptr != NULL) {
zval_ptr_dtor(retval_ptr);
}
php_request_shutdown(TSRMLS_C);
tsrm_set_interpreter_context(old);
tsrm_free_interpreter_context(tsrm_ls);
} else {
error = -2;
}
//zend_fcall_info_args_clear(&uv->callback[type]->fci, 0); //zend_fcall_info_args_clear(&uv->callback[type]->fci, 0);
return error; return error;
} }
#else
static int php_uv_do_callback3(zval **retval_ptr, php_uv_t *uv, zval ***params, int param_count, enum php_uv_callback_type type)
{
}
#endif
static void php_uv_tcp_connect_cb(uv_connect_t *req, int status) static void php_uv_tcp_connect_cb(uv_connect_t *req, int status)
{ {
@ -1628,7 +1704,7 @@ static void php_uv_async_cb(uv_async_t* handle, int status)
params[0] = &resource; params[0] = &resource;
params[1] = &zstat; params[1] = &zstat;
php_uv_do_callback2(&retval_ptr, uv, params, 2, PHP_UV_ASYNC_CB TSRMLS_CC); php_uv_do_callback2(&retval_ptr, uv, params, 2, PHP_UV_ASYNC_CB TSRMLS_CC);
zval_ptr_dtor(&resource); zval_ptr_dtor(&resource);
@ -1650,11 +1726,8 @@ static void php_uv_work_cb(uv_work_t* req)
PHP_UV_DEBUG_PRINT("work_cb\n"); PHP_UV_DEBUG_PRINT("work_cb\n");
php_uv_do_callback2(&retval_ptr, uv, NULL, 0, PHP_UV_WORK_CB TSRMLS_CC); php_uv_do_callback3(&retval_ptr, uv, NULL, 0, PHP_UV_WORK_CB);
if (retval_ptr != NULL) {
zval_ptr_dtor(&retval_ptr);
}
PHP_UV_DEBUG_RESOURCE_REFCOUNT(uv_work_cb, uv->resource_id); PHP_UV_DEBUG_RESOURCE_REFCOUNT(uv_work_cb, uv->resource_id);
} }
@ -1664,12 +1737,13 @@ static void php_uv_after_work_cb(uv_work_t* req, int status)
php_uv_t *uv = (php_uv_t*)req->data; php_uv_t *uv = (php_uv_t*)req->data;
TSRMLS_FETCH_FROM_CTX(uv != NULL ? uv->thread_ctx : NULL); TSRMLS_FETCH_FROM_CTX(uv != NULL ? uv->thread_ctx : NULL);
uv = (php_uv_t*)req->data;
PHP_UV_DEBUG_PRINT("after_work_cb\n"); PHP_UV_DEBUG_PRINT("after_work_cb\n");
php_uv_do_callback2(&retval_ptr, uv, NULL, 0, PHP_UV_AFTER_WORK_CB TSRMLS_CC); php_uv_do_callback3(&retval_ptr, uv, NULL, 0, PHP_UV_AFTER_WORK_CB);
zval_ptr_dtor(&retval_ptr); PHP_UV_DEBUG_RESOURCE_REFCOUNT(uv_work_cb, uv->resource_id);
PHP_UV_DEBUG_RESOURCE_REFCOUNT(uv_after_work_cb, uv->resource_id);
} }
static void php_uv_fs_cb(uv_fs_t* req) static void php_uv_fs_cb(uv_fs_t* req)
@ -5537,6 +5611,7 @@ PHP_FUNCTION(uv_async_send)
*/ */
PHP_FUNCTION(uv_queue_work) PHP_FUNCTION(uv_queue_work)
{ {
#ifdef ZTS
int r; int r;
zval *zloop = NULL; zval *zloop = NULL;
uv_loop_t *loop; uv_loop_t *loop;
@ -5564,6 +5639,9 @@ PHP_FUNCTION(uv_queue_work)
php_error_docref(NULL TSRMLS_CC, E_ERROR, "uv_queue_work failed"); php_error_docref(NULL TSRMLS_CC, E_ERROR, "uv_queue_work failed");
return; return;
} }
#else
php_error_docref(NULL TSRMLS_CC, E_ERROR, "uv_queue_work doesn't support this PHP. please rebuild with --enable-maintainer-zts");
#endif
} }
/* }}} */ /* }}} */

View File

@ -40,7 +40,18 @@
#include "ext/sockets/php_sockets.h" #include "ext/sockets/php_sockets.h"
#endif #endif
#include "zend_interfaces.h" #include <Zend/zend.h>
#include <Zend/zend_compile.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_extensions.h>
#include <Zend/zend_globals.h>
#include <Zend/zend_hash.h>
#include <Zend/zend_ts_hash.h>
#include <Zend/zend_interfaces.h>
#include <Zend/zend_list.h>
#include <Zend/zend_object_handlers.h>
#include <Zend/zend_variables.h>
#include <Zend/zend_vm.h>
/* Define the entry point symbol /* Define the entry point symbol
* Zend will use when loading this module * Zend will use when loading this module

View File

@ -1,21 +1,45 @@
--TEST-- --TEST--
Check for fs read and close Check for poll read and close
--FILE-- --FILE--
<?php <?php
$fd = fopen("testfile","w+"); $socket = stream_socket_server("tcp://0.0.0.0:9999", $errno, $errstr);
stream_set_blocking($socket, 0);
$poll = uv_poll_init(uv_default_loop(), $socket);
uv_poll_start($poll, UV::READABLE, function($poll, $stat, $ev, $socket){
$conn = stream_socket_accept($socket);
uv_poll_stop($poll);
$pp = uv_poll_init(uv_default_loop(), $conn);
uv_poll_start($pp, UV::WRITABLE, function($poll, $stat, $ev, $conn) use (&$pp){
$poll = uv_poll_init(uv_default_loop(), $fd);
uv_poll_start($poll, UV::WRITABLE, function($poll, $stat, $ev, $conn){
fwrite($conn, "Hello");
fclose($conn);
$data = file_get_contents("testfile");
if ($data == "Hello") {
echo "OK";
}
unlink("testfile");
uv_poll_stop($poll); uv_poll_stop($poll);
uv_fs_write(uv_default_loop(), $conn, "OK", -1, function($conn, $nwrite){
fclose($conn);
});
});
}); });
$address = uv_ip4_addr("0.0.0.0","9999");
$tcp = uv_tcp_init();
uv_tcp_connect($tcp, $address, function($client, $stat){
$request = <<<EOF
HELO
EOF;
uv_write($client,$request,function($client, $stat){
if ($stat == 0) {
uv_read_start($client,function($client, $nread, $buffer){
echo "$buffer\n";
//var_dump($buffer);
uv_close($client);
});
} else {
uv_close($client);
}
});
});
uv_run(); uv_run();
--EXPECT-- --EXPECT--
OK OK

View File

@ -1,5 +1,13 @@
--TEST-- --TEST--
Check for uv_queue_work Check for uv_queue_work
--SKIPIF--
<?php
ob_start();
phpinfo();
$data = ob_get_clean();
if (!preg_match("/Thread Safety.+?enabled/", $data)) {
echo "skip";
}
--FILE-- --FILE--
<?php <?php
$loop = uv_default_loop(); $loop = uv_default_loop();