rfc:zend-vm-pause-api

This is an old revision of the document!


PHP RFC: Zend VM Pause API

Introduction

There is no API for change the Zend VM's execute flow. The only approach to change the Zend VM's execute flow is introducing a new keyword and making a new opline, and use ZEND_VM_RETURN/ZEND_VM_CONTINUE macro in its handler.

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.

Proposal

This PRFC propose a new zend_vm_pause API and an new EG(pause_op), by which some zend extension could make the zend vm execution paused. As a result, 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

static void zend_init_pause_op(void)
{
	memset(&EG(pause_op), 0, sizeof(zend_op));
 
	EG(pause_op).opcode = ZEND_HANDLE_PAUSE;
	EG(pause_op).op1_type = IS_UNUSED;
 	EG(pause_op).op2_type = IS_UNUSED;
 	EG(pause_op).result_type = IS_UNUSED;
 
	ZEND_VM_SET_OPCODE_HANDLER(&EG(pause_op));
}

A new opcode ZEND_HANDLE_PAUSE will also be need, and its handle looks like

ZEND_VM_HANDLER(198, ZEND_HANDLE_PAUSE, ANY, ANY)
{
	void (*fn)(const zend_op *opline, zend_execute_data *execute_data);
 
	USE_OPLINE;
 
	// use pause_op's result to save a function pointer as callback
	// so we can do something in extension before paused
	fn = *((void**)&opline->result);
 
	// restore the opline pointer
	opline = *((const zend_op**)&opline->op1);
 
	// execute the callback function
	if (EXPECTED(fn != NULL)) {
		fn(opline, execute_data);
	}
 
	SAVE_OPLINE();
 
	// make vm paused here
	ZEND_VM_RETURN();
}

The zend_vm_pause API is like this

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++ */
}

You will see that we want to make the Zend VM execute the EG(pause_op) by changing the EX(prev_execute_data)->opline pointer.

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();
}

Backward Incompatible Changes

None

Proposed PHP Version(s)

PHP 7.3

But I would like this patch could be backported to PHP 7.1 and PHP 7.2.

RFC Impact

Any internal function call will LOAD_OPLINE() twice. And this will make slow the execution, theoretically.

To SAPIs

None

To Existing Extensions

None

To Opcache

None

New Constants

None

php.ini Defaults

None

Open Issues

Make sure there are no open issues when the vote starts!

Unaffected PHP Functionality

List existing areas/features of PHP that will not be changed by the RFC.

This helps avoid any ambiguity, shows that you have thought deeply about the RFC's impact, and helps reduces mail list noise.

Future Scope

This sections details areas where the feature might be improved in future, but that are not currently proposed in this RFC.

Proposed Voting Choices

50%+1

Patches and Tests

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged to
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

Links to external references, discussions or RFCs

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/zend-vm-pause-api.1509498895.txt.gz · Last modified: 2017/11/01 01:14 by lvht