Skip to content

Commit

Permalink
Fix Class "X" not found due to coroutine context switching in autoload (
Browse files Browse the repository at this point in the history
#5180)

* Fix Class "X" not found due to coroutine context switching in autoload

* Fix

* Fix

* Support coroutine switch in autoload

* Update

* Add test

* Fix

* fix test name

* Fix

* fix

* optimize

* optimize
  • Loading branch information
Yurunsoft authored Nov 14, 2023
1 parent 348a5ff commit 6f1df03
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 0 deletions.
71 changes: 71 additions & 0 deletions ext-src/swoole_coroutine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,66 @@ void* PHPCoroutine::stack_base(PHPContext *ctx)
}
#endif /* ZEND_CHECK_STACK_LIMIT */

/* hook autoload */

static zend_class_entry *(*original_zend_autoload)(zend_string *name, zend_string *lc_name);

struct AutoloadContext {
Coroutine *coroutine;
zend_class_entry *ce;
};

struct AutoloadQueue {
Coroutine *coroutine;
std::queue<AutoloadContext *> *queue;
};

static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name)
{
auto current = Coroutine::get_current();
if (!current) {
return original_zend_autoload(name, lc_name);
}

ZEND_ASSERT(EG(in_autoload) != nullptr);
zend_hash_del(EG(in_autoload), lc_name);

if (UNEXPECTED(SWOOLE_G(in_autoload) == nullptr)) {
ALLOC_HASHTABLE(SWOOLE_G(in_autoload));
zend_hash_init(SWOOLE_G(in_autoload), 8, nullptr, nullptr, 0);
}
zval *z_queue = zend_hash_find(SWOOLE_G(in_autoload), lc_name);
if (z_queue != nullptr) {
auto queue = (AutoloadQueue *) Z_PTR_P(z_queue);
if (queue->coroutine == current) {
return nullptr;
}
AutoloadContext context;
context.coroutine = current;
context.ce = nullptr;
queue->queue->push(&context);
current->yield();
return context.ce;
}
AutoloadQueue queue;
queue.coroutine = current;
std::queue<AutoloadContext *> queue_object;
queue.queue = &queue_object;

zend_hash_add_ptr(SWOOLE_G(in_autoload), lc_name, &queue);
zend_class_entry *ce = original_zend_autoload(name, lc_name);
zend_hash_del(SWOOLE_G(in_autoload), lc_name);

AutoloadContext *pending_context = nullptr;
while (!queue_object.empty()) {
pending_context = queue_object.front();
queue_object.pop();
pending_context->ce = ce;
pending_context->coroutine->resume();
}
return ce;
}

void php_swoole_coroutine_minit(int module_number) {
SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", "Co", swoole_coroutine_methods, nullptr);
SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny);
Expand All @@ -920,6 +980,11 @@ void php_swoole_coroutine_minit(int module_number) {

SW_REGISTER_LONG_CONSTANT("SWOOLE_EXIT_IN_COROUTINE", SW_EXIT_IN_COROUTINE);
SW_REGISTER_LONG_CONSTANT("SWOOLE_EXIT_IN_SERVER", SW_EXIT_IN_SERVER);

/* hook autoload */
original_zend_autoload = zend_autoload;
zend_autoload = swoole_coroutine_autoload;
SWOOLE_G(in_autoload) = nullptr;
}

void php_swoole_coroutine_rinit() {
Expand All @@ -938,6 +1003,12 @@ void php_swoole_coroutine_rinit() {
}

void php_swoole_coroutine_rshutdown() {
if (SWOOLE_G(in_autoload)) {
zend_hash_destroy(SWOOLE_G(in_autoload));
FREE_HASHTABLE(SWOOLE_G(in_autoload));
SWOOLE_G(in_autoload) = nullptr;
}

PHPCoroutine::shutdown();
}

Expand Down
1 change: 1 addition & 0 deletions php_swoole.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole)
zend_bool enable_fiber_mock;
long socket_buffer_size;
int req_status;
HashTable *in_autoload;
ZEND_END_MODULE_GLOBALS(swoole)
// clang-format on

Expand Down
2 changes: 2 additions & 0 deletions tests/include/api/test_classes/A2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?php
new SwooleTestClassA2();
32 changes: 32 additions & 0 deletions tests/swoole_coroutine/autoload.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
swoole_coroutine: autoload
--SKIPIF--
<?php
require __DIR__ . '/../include/skipif.inc';
?>
--FILE--
<?php
require __DIR__ . '/../include/bootstrap.php';

spl_autoload_register(function ($class) {
co::sleep(0.001); // coroutine context switch
if ($class == 'SwooleTestClassA') {
require TESTS_ROOT_PATH . '/include/api/test_classes/A.php';
}
});

Swoole\Coroutine\run(function () {
for ($i = 0; $i < 2; ++$i)
{
go(static function (): void {
var_dump(new SwooleTestClassA());
});
}
});

?>
--EXPECTF--
object(SwooleTestClassA)#%d (0) {
}
object(SwooleTestClassA)#%d (0) {
}
28 changes: 28 additions & 0 deletions tests/swoole_coroutine/autoload_not_found.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
swoole_coroutine: autoload not found
--SKIPIF--
<?php
require __DIR__ . '/../include/skipif.inc';
?>
--FILE--
<?php
require __DIR__ . '/../include/bootstrap.php';

spl_autoload_register(function ($class) {
if ($class == 'SwooleTestClassA2') {
require TESTS_ROOT_PATH . '/include/api/test_classes/A2.php';
}
});

Co\run( function() {
var_dump(new SwooleTestClassA2());
});
?>
--EXPECTF--
Fatal error: Uncaught Error: Class "SwooleTestClassA2" not found in %s:%d
Stack trace:
#0 %s(%d): %s
#1 %s(%d): %s
#2 [internal function]: {closure}()
#3 {main}
thrown in %s on line %d
26 changes: 26 additions & 0 deletions tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
swoole_coroutine: autoload not found and not in coroutine
--SKIPIF--
<?php
require __DIR__ . '/../include/skipif.inc';
?>
--FILE--
<?php
require __DIR__ . '/../include/bootstrap.php';

spl_autoload_register(function ($class) {
if ($class == 'SwooleTestClassA2') {
require TESTS_ROOT_PATH . '/include/api/test_classes/A2.php';
}
});

var_dump(new SwooleTestClassA2());

?>
--EXPECTF--
Fatal error: Uncaught Error: Class "SwooleTestClassA2" not found in %s:%d
Stack trace:
#0 %s(%d): %s
#1 %s(%d): %s
#2 {main}
thrown in %s on line %d
25 changes: 25 additions & 0 deletions tests/swoole_coroutine/autoload_not_in_coroutine.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
swoole_coroutine: autoload not in coroutine
--SKIPIF--
<?php
require __DIR__ . '/../include/skipif.inc';
?>
--FILE--
<?php
require __DIR__ . '/../include/bootstrap.php';

spl_autoload_register(function ($class) {
if ($class == 'SwooleTestClassA') {
require TESTS_ROOT_PATH . '/include/api/test_classes/A.php';
}
});

var_dump(new SwooleTestClassA());
var_dump(new SwooleTestClassA());

?>
--EXPECTF--
object(SwooleTestClassA)#%d (0) {
}
object(SwooleTestClassA)#%d (0) {
}

0 comments on commit 6f1df03

Please sign in to comment.