rfc:zend-vm-pause-api

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
rfc:zend-vm-pause-api [2017/11/01 01:22] lvhtrfc:zend-vm-pause-api [2017/11/05 12:56] lvht
Line 2: Line 2:
   * Version: 0.9   * Version: 0.9
   * Date: 2017-11-01   * Date: 2017-11-01
-  * Author: Haitao Lv php@lvht.net+  * Author: Haitao Lvphp@lvht.net
   * Status: Draft   * Status: Draft
   * First Published at: http://wiki.php.net/rfc/zend-vm-pause-api   * First Published at: http://wiki.php.net/rfc/zend-vm-pause-api
 +
  
 ===== Introduction ===== ===== Introduction =====
Line 11: Line 12:
  
 So it is impossible to change the Zend VM's execute flow in an zend extension. As a result, any feature, like Fiber, So it is impossible to change the Zend VM's execute flow in an zend extension. As a result, any feature, like Fiber,
-related to Zend VM's execute flow cannot be implemented by a standalone.+related to Zend VM's execute flow cannot be implemented by a standalone extension.
  
 ===== Proposal ===== ===== Proposal =====
-This PRFC propose a new zend_vm_pause API and an new EG(pause_op), by which some zend extension could make the +This PRFC propose a new vm_interrupt type, by which some zend extension could make the zend vm execution pause and return. 
-zend vm execution paused As a result, we can implement feature like Fiber in a standalone extension.+So we can implement feature like Fiber in a standalone extension.
  
-We need introduce a new vm global EG(pause_op) as zend_op and init it by the following code 
 <code c> <code c>
-static void zend_init_pause_op(void) +diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h 
-+index 183072033607..bd7408e824fc 100644 
- memset(&EG(pause_op), 0, sizeof(zend_op)); +--- a/Zend/zend_vm_def.h 
- ++++ b/Zend/zend_vm_def.h 
- EG(pause_op).opcode = ZEND_HANDLE_PAUSE; +@@ -8893,13 +8893,19 @@ ZEND_VM_DEFINE_OP(137ZEND_OP_DATA); 
- EG(pause_op).op1_type = IS_UNUSED; +  
-  EG(pause_op).op2_type = IS_UNUSED; + ZEND_VM_HELPER(zend_interrupt_helperANY, ANY
-  EG(pause_op).result_type = IS_UNUSED; + { 
- ++ int8_t interrupt_type = EG(vm_interrupt); 
- ZEND_VM_SET_OPCODE_HANDLER(&EG(pause_op)); ++ 
-+  EG(vm_interrupt) = 0
-</code> +  if (EG(timed_out)) { 
- +  zend_timeout(0); 
-A new opcode ZEND_HANDLE_PAUSE will also be need, and its handle looks like +  else if (zend_interrupt_function{ 
-<code c> +  SAVE_OPLINE(); 
-ZEND_VM_HANDLER(198, ZEND_HANDLE_PAUSE, ANY, ANY) +  zend_interrupt_function(execute_data); 
-+- ZEND_VM_ENTER(); 
- void (*fn)(const zend_op *opline, zend_execute_data *execute_data); ++ if (UNEXPECTED(interrupt_type == 2)) { 
- ++ ZEND_VM_RETURN(); 
- USE_OPLINE; ++ } else { 
- ++ ZEND_VM_ENTER(); 
- // use pause_op's result to save function pointer as callback ++ } 
- // so we can do something in extension before paused + 
- fn = *((void**)&opline->result); +  ZEND_VM_CONTINUE(); 
-  + }
- // restore the opline pointer +
- opline = *((const zend_op**)&opline->op1); +
- +
- // execute the callback function +
- if (EXPECTED(fn != NULL)) { +
- fn(oplineexecute_data); +
- } +
- +
- SAVE_OPLINE(); +
- +
- // make vm paused here +
- ZEND_VM_RETURN(); +
-+
-</code> +
- +
-The zend_vm_pause API is like this +
-<code c> +
-ZEND_API ZEND_COLD void zend_vm_pause(void (*fn)(const zend_op*zend_execute_data*)) /* {{{ */ +
-+
- zend_execute_data *execute_data = EG(current_execute_data); +
- +
- // use EG(paused_op).op1 and EG(paused_op).op2 to save the next opline pointer +
- // and the ZEND_HANDLE_PAUSE will resume opline to this +
- *((const zend_op**)&EG(pause_op).op1) = EX(prev_execute_data)->opline + 1+
-  +
- // use EG(paused_op).result and EG(paused_op).extended_value to save the callback function pointer +
- *((void**)&EG(pause_op).result) = fn; +
- +
- // make the next opline point to EG(pause_op) +
- // so that Zend VM will execute it after the DO_FCALL opline +
- EX(prev_execute_data)->opline = (&EG(pause_op)) - 1/* zend_vm will do opline++ */ +
-} +
-</code> +
- +
-You will see that we want to make the Zend VM execute the EG(pause_opby changing the EX(prev_execute_data)->opline pointer. +
- +
-However, it does not work out of box. The reason is under the ZEND_DO_FCALL's handler. +
-<code c> +
-ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL)+
-+
-        // ... +
- +
-        // load opline from EG(current_execute_data).opline +
-        LOAD_OPLINE(); +
- +
-        if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { +
-                // ... +
-        } else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) { +
-                // ... +
-                 +
-                // the extension's method will enter this branch +
-                // after its execution, we changed the EG(current_execute_data).opline +
-                // However, as the value of EG(current_execute_data).opline has already been loade into the opline +
-                 +
-                // We need to reload the opline +
-                LOAD_OPLINE(); // load the opline again +
- +
-                // ... +
-        } else { +
-                // ... +
-        } +
- +
-        // ... +
- +
-        // so the opline 1 will point to our EG(pause_op+
-        ZEND_VM_SET_OPCODE(opline 1); +
-        ZEND_VM_CONTINUE(); +
-}+
 </code> </code>
  
Line 123: Line 55:
  
 ===== RFC Impact ===== ===== RFC Impact =====
-Any internal function call will LOAD_OPLINE() twice. And this will make slow the execution, theoretically.+None
 ==== To SAPIs ==== ==== To SAPIs ====
 None None
Line 154: Line 86:
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
-https://github.com/php/php-src/pull/2886+https://github.com/php/php-src/pull/2902
  
 ===== Implementation ===== ===== Implementation =====
rfc/zend-vm-pause-api.txt · Last modified: 2017/11/21 10:56 by lvht