-
-
Notifications
You must be signed in to change notification settings - Fork 466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Zend API wherever possible #1415
Conversation
Ported from: https://github.com/sergeyklay/issue1394/pull/6 Thanks to: @sjinks, @dreamsxin, @ovr, @Jurigag Issue: #1392
Yes, sure - this is why I closed that PR and went to bed :-) |
@sjinks Lets continue here instead of example repo |
It looks like test_globals->cache_enabled = 0; solves the issue. Back to the drawing board :-( |
If we disable cache slots, everything works. diff --git a/kernels/ZendEngine3/fcall.c b/kernels/ZendEngine3/fcall.c
index 0e920e6..75fe800 100644
--- a/kernels/ZendEngine3/fcall.c
+++ b/kernels/ZendEngine3/fcall.c
@@ -206,15 +206,15 @@ int zephir_call_user_function(zval *object_pp, zend_class_entry *obj_ce, zephir_
}
if ((!cache_entry || !*cache_entry) && zephir_globals_ptr->cache_enabled) {
- if (cache_slot > 0) {
- if (zephir_globals_ptr->scache[cache_slot]) {
- reload_cache = 0;
- temp_cache_entry = zephir_globals_ptr->scache[cache_slot];
- if (cache_entry) {
- *cache_entry = temp_cache_entry;
- }
- }
- }
+// if (cache_slot > 0) {
+// if (zephir_globals_ptr->scache[cache_slot]) {
+// reload_cache = 0;
+// temp_cache_entry = zephir_globals_ptr->scache[cache_slot];
+// if (cache_entry) {
+// *cache_entry = temp_cache_entry;
+// }
+// }
+// }
if (reload_cache) {
fcall_key_hash = zephir_make_fcall_key(&fcall_key, &fcall_key_len, (object_pp && type != zephir_fcall_ce ? Z_OBJCE_P(object_pp) : obj_ce), type, function_name); |
I would say the issue is in this part: if (cache_entry && *cache_entry) {
/* We have a cache record, initialize scope */
fcic.initialized = 1;
fcic.calling_scope = obj_ce;
if (object_pp) {
fcic.called_scope = Z_OBJCE_P(object_pp);
}
else {
zend_class_entry *called_scope;
#if PHP_VERSION_ID >= 70100
called_scope = zend_get_called_scope(EG(current_execute_data));
#else
called_scope = EG(current_execute_data)->called_scope;
#endif
if (obj_ce && (!called_scope || instanceof_function(called_scope, obj_ce))) {
fcic.called_scope = obj_ce;
}
else {
fcic.called_scope = called_scope;
}
}
fcic.object = object_pp ? Z_OBJ_P(object_pp) : NULL;
#ifndef ZEPHIR_RELEASE
fcic.function_handler = (*cache_entry)->f;
++(*cache_entry)->times;
#else
fcic.function_handler = *cache_entry;
#endif
} This basically corresponds to the following code from zend_fcall_info_cache fcic;
ZVAL_UNDEF(&fci.function_name); /* Unused */
fcic.initialized = 1;
if (!obj_ce) {
obj_ce = object ? Z_OBJCE_P(object) : NULL;
}
if (!fn_proxy || !*fn_proxy) {
HashTable *function_table = obj_ce ? &obj_ce->function_table : EG(function_table);
fcic.function_handler = zend_hash_str_find_ptr(
function_table, function_name, function_name_len);
if (fcic.function_handler == NULL) {
/* error at c-level */
zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s", obj_ce ? ZSTR_VAL(obj_ce->name) : "", obj_ce ? "::" : "", function_name);
}
if (fn_proxy) {
*fn_proxy = fcic.function_handler;
}
} else {
fcic.function_handler = *fn_proxy;
}
fcic.calling_scope = obj_ce;
if (object) {
fcic.called_scope = Z_OBJCE_P(object);
} else {
zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
if (obj_ce &&
(!called_scope ||
!instanceof_function(called_scope, obj_ce))) {
fcic.called_scope = obj_ce;
} else {
fcic.called_scope = called_scope;
}
}
fcic.object = object ? Z_OBJ_P(object) : NULL; |
With caching: Test\Oo\OoDynamicA::getnew Without caching: Test\Oo\OoDynamicA::call1 => self::call2 => self::getnew |
I have added the following debug code: fcic.initialized = 0;
zval tmp;
zephir_var_export_ex(&tmp, function_name);
fprintf(stderr, "> %s called: %p, calling: %p, obj: %p\n", Z_STRVAL(tmp), fcic.called_scope, fcic.calling_scope, fcic.object);
status = zend_call_function(&fci, &fcic);
fprintf(stderr, "< called: %p, calling: %p, obj: %p\n", fcic.called_scope, fcic.calling_scope, fcic.object); With With
Without:
|
Test case: $t = new \Test\Oo();
$obj9 = $t->testInstance9();
$this->assertTrue(is_object($obj9));
$this->assertInstanceOf('Test\Oo\OoDynamicA', $obj9);
$obj10 = $t->testInstance10();
$this->assertTrue(is_object($obj10));
$this->assertInstanceOf('Test\Oo\OoDynamicB', $obj10); |
GOOD:
BAD:
In the BAD case, the function handler for Since the bug manifests itself only in ZE3, it makes me think else if (type == zephir_fcall_static) {
#if PHP_VERSION_ID >= 70100
calling_scope = zend_get_called_scope(EG(current_execute_data));
#else
calling_scope = EG(current_execute_data)->called_scope;
#endif
if (UNEXPECTED(!calling_scope)) {
return 0;
}
} |
@sjinks I think we can skip optimization through caching right now for >= 70100 |
Actually, this is not a bug in the Kernel, this is a bug in the translator. PHP_METHOD(Test_Oo, testInstance9) {
zval o;
int ZEPHIR_LAST_CALL_STATUS;
zephir_fcall_cache_entry *_0 = NULL;
ZEPHIR_INIT_THIS();
ZVAL_UNDEF(&o);
ZEPHIR_MM_GROW();
ZEPHIR_CALL_CE_STATIC(&o, test_oo_oodynamica_ce, "getnew", &_0, 48);
zephir_check_call_status();
RETURN_CCTOR(o);
}
PHP_METHOD(Test_Oo, testInstance10) {
zval o;
int ZEPHIR_LAST_CALL_STATUS;
zephir_fcall_cache_entry *_0 = NULL;
ZEPHIR_INIT_THIS();
ZVAL_UNDEF(&o);
ZEPHIR_MM_GROW();
ZEPHIR_CALL_CE_STATIC(&o, test_oo_oodynamicb_ce, "getnew", &_0, 48);
zephir_check_call_status();
RETURN_CCTOR(o);
} Both methods share the same cache slot (48), yet they are called from different contexts. |
@sergeyklay as far as I can tell, caching issue affects ZE3 (that is, PHP_VERSION_ID >= 70000) :-( |
Just tested ZE2: Test\Oo\OoDynamicA::getnew and Test\Oo\OoDynamicB::getnew do have the same function handler. In ZE3 this is no longer the case. |
|
@sjinks As I can see we can keep caching for ZE2 and do not use for ZE3. At least for first time, to localize issue. |
@sjinks What do you think about disabling caching at all for ZE3? |
Probably we will have to do that :-( I think I have found the place to fix in the compiler but I don't know its internals good enough // StaticMethodCache::get()
$mustBeCached = false;
if (!$compilationContext->insideCycle) {
if (!($method instanceof \ReflectionMethod)) {
$classDefinition = $method->getClassDefinition();
if (!$classDefinition->isBundled() && $allowNtsCache) {
$mustBeCached = true;
} else {
if (!$method->isPrivate() && !$method->isFinal()) {
return 'NULL, 0';
}
}
} else {
if (!$method->isPrivate() && !$method->isFinal()) {
return 'NULL, 0';
}
}
} The place where What I wanted to do was to find out if |
It is called from |
@sjinks Let me see what we can |
Well after all changes it would be nice to at least test it on php7 with those changes performance if it's affected by any way. |
1702b29
to
dbe32f9
Compare
I think / hope we are good to go now :-) |
@sjinks Let me 15 min to provide some test |
#1339 could you add some test for this? |
@Jurigag We’ll use for this a separated PR |
@sjinks Good work! Thank you 👍 |
Those changes are causing some pdo-related weird issues: https://travis-ci.org/phalcon/cphalcon/jobs/214812313 Like object with PDO connection is never destroyed - causing pdo connection to stay for all the time. Any idea? @sjinks |
Sqlite? Number of connections exceeded? Sounds weird... |
On sqlite it's locked error - i guess the same problem like number of connections, on mysql it hits later this error on running codeception tests. It's exactly happening after this PR. |
Let's go there - #1417 |
Ported from: https://github.com/sergeyklay/issue1394/pull/6. Thanks to: @sjinks
Refs: #1392