Swoole中参数持久化以及给闭包对象引用加一的作用?

在阅读Swoole定时器源码的时候,php_swoole_timer_add里面:
发现如果是调用了timer::after接口的话,那么话进行参数持久化的操作:

sw_zend_fci_params_persist(&fci->fci);

如果是调用了timer::tick的话,就不需要进行参数持久化的操作。
并且,最后还调用了sw_zend_fci_cache_persist来对闭包进行引用计数加一的操作。
我想知道为什么需要做这两个操作?如果不做这两个操作,会触发什么Bug呢?能否用一段完整的PHP代码来触发一下这个Bug呢?并且解释一下为什么会触发Bug。我想看看差异在哪,以及什么时候需要调用这两个函数。
目前,我写了一段简单的脚本:

<?php

namespace Swoole;

$str = "test";

Timer::after(1000, function() use ($str){
    echo "timeout, $str\n";
});

当我注释掉sw_zend_fci_cache_persist(&fci->fci_cache);的时候,会在PHP协程入口函数里面进入ZEND_INTERNAL_FUNCTION这个分支:

else /* ZEND_INTERNAL_FUNCTION */
{
    ZVAL_NULL(retval);
    call->prev_execute_data = NULL;
    call->return_value = NULL; /* this is not a constructor call */
    execute_internal(call, retval);
    zend_vm_stack_free_args(call);
}

然后报Segmentation fault错误。如果不注释掉sw_zend_fci_cache_persist(&fci->fci_cache);,那么就可以顺利的进入ZEND_USER_FUNCTION分支:

if (EXPECTED(func->type == ZEND_USER_FUNCTION))
{
    ZVAL_UNDEF(retval);
    // TODO: enhancement it, separate execute data is necessary, but we lose the backtrace
    EG(current_execute_data) = NULL;
#if PHP_VERSION_ID >= 70200
    zend_init_func_execute_data(call, &func->op_array, retval);
#else
    zend_init_execute_data(call, &func->op_array, retval);
#endif
    zend_execute_ex(EG(current_execute_data));
}

但是我不知道其中的原因。

阅读 1.6k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题