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
rfc:zend-vm-pause-api [2017/11/01 01:22] lvhtrfc:zend-vm-pause-api [2017/11/21 10:56] (current) 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: Obsolete
   * First Published at: http://wiki.php.net/rfc/zend-vm-pause-api   * First Published at: http://wiki.php.net/rfc/zend-vm-pause-api
 +
 +This RPC has been obsolete. Please see https://github.com/php/php-src/pull/2902 for more detail.
  
 ===== Introduction ===== ===== Introduction =====
Line 11: Line 13:
  
 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 56:
  
 ===== 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 87:
  
 ===== 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.1509499344.txt.gz · Last modified: 2017/11/01 01:22 by lvht