TrueAsync ABI
TrueAsync ABI побудований на чіткому розділенні визначення та реалізації:
| Шар | Розташування | Відповідальність |
|---|---|---|
| Zend Engine | Zend/zend_async_API.h | Визначення типів, структур, вказівників на функції |
| Розширення | ext/async/ | Реалізація всіх функцій, реєстрація через API |
Ядро PHP не викликає функції розширення напряму. Натомість воно використовує макроси ZEND_ASYNC_*, що викликають вказівники на функції, зареєстровані розширенням під час завантаження.
Цей підхід переслідує дві цілі:
- Асинхронний рушій може працювати з будь-якою кількістю розширень, що реалізують
ABI - Макроси зменшують залежність від деталей реалізації та мінімізують рефакторинг
Глобальний стан
Частина глобального стану, пов'язана з асинхронністю, знаходиться в ядрі PHP і також доступна через макрос ZEND_ASYNC_G(v), а також інші спеціалізовані макроси, наприклад ZEND_ASYNC_CURRENT_COROUTINE.
typedef struct {
zend_async_state_t state; // OFF -> READY -> ACTIVE
zend_atomic_bool heartbeat; // Прапорець серцебиття scheduler
bool in_scheduler_context; // TRUE, якщо зараз в контексті scheduler
bool graceful_shutdown; // TRUE під час завершення
unsigned int active_coroutine_count;
unsigned int active_event_count;
zend_coroutine_t *coroutine; // Поточна корутина
zend_async_scope_t *main_scope; // Кореневий scope
zend_coroutine_t *scheduler; // Корутина scheduler
zend_object *exit_exception;
zend_async_heartbeat_handler_t heartbeat_handler;
} zend_async_globals_t;Запуск
Наразі TrueAsync запускається не одразу, а ліниво у "правильний" момент. (Цей підхід зміниться в майбутньому, оскільки практично будь-яка PHP I/O-функція активує Scheduler.)
Коли PHP-скрипт починає виконання, TrueAsync перебуває в стані ZEND_ASYNC_READY. При першому виклику функції, що потребує Scheduler, через макрос ZEND_ASYNC_SCHEDULER_LAUNCH(), scheduler ініціалізується і переходить у стан ZEND_ASYNC_ACTIVE.
В цей момент код, що виконувався, опиняється в головній корутині, а для Scheduler створюється окрема корутина.
Окрім ZEND_ASYNC_SCHEDULER_LAUNCH(), що явно активує Scheduler, TrueAsync також перехоплює управління у функціях php_execute_script_ex та php_request_shutdown.
// php_execute_script_ex
if (prepend_file_p && result) {
result = zend_execute_script(ZEND_REQUIRE, NULL, prepend_file_p) == SUCCESS;
}
if (result) {
result = zend_execute_script(ZEND_REQUIRE, retval, primary_file) == SUCCESS;
}
if (append_file_p && result) {
result = zend_execute_script(ZEND_REQUIRE, NULL, append_file_p) == SUCCESS;
}
ZEND_ASYNC_RUN_SCHEDULER_AFTER_MAIN();
ZEND_ASYNC_INITIALIZE;Цей код дозволяє передати управління Scheduler після завершення виконання головного потоку. Scheduler, у свою чергу, може запустити інші корутини, якщо такі є.
Цей підхід забезпечує не лише 100% прозорість TrueAsync для PHP-програміста, а й повну сумісність із PHP SAPI. Клієнти, що використовують PHP SAPI, продовжують сприймати PHP як синхронний, навіть якщо всередині працює EventLoop.
У функції php_request_shutdown відбувається фінальне перехоплення для виконання корутин у деструкторах, після чого Scheduler завершує роботу і звільняє ресурси.
Реєстрація розширень
Оскільки TrueAsync ABI є частиною ядра PHP, він доступний усім PHP-розширенням на найранішому етапі. Тому розширення мають можливість правильно ініціалізувати TrueAsync до того, як PHP Engine буде запущений для виконання коду.
Розширення реєструє свої реалізації через набір функцій _register(). Кожна функція приймає набір вказівників на функції і записує їх до глобальних extern-змінних ядра.
Залежно від цілей розширення, allow_override дозволяє легально перереєструвати вказівники на функції. За замовчуванням TrueAsync забороняє двом розширенням визначати одні й ті самі API-групи.
TrueAsync поділяється на декілька категорій, кожна з власною функцією реєстрації:
Scheduler-- API, пов'язаний з основною функціональністю. Містить більшість різноманітних функційReactor-- API для роботи зEvent loopта подіями. Містить функції для створення різних типів подій та управління життєвим циклом реактораThreadPool-- API для управління пулом потоків та чергою завданьAsync IO-- API для асинхронного введення/виведення, включаючи файлові дескриптори, сокети та UDPPool-- API для управління універсальними пулами ресурсів з підтримкою healthcheck та circuit breaker
zend_async_scheduler_register(
char *module, // Ім'я модуля
bool allow_override, // Дозволити перезапис
zend_async_scheduler_launch_t, // Запуск scheduler
zend_async_new_coroutine_t, // Створення корутини
zend_async_new_scope_t, // Створення scope
zend_async_new_context_t, // Створення контексту
zend_async_spawn_t, // Запуск корутини
zend_async_suspend_t, // Призупинення
zend_async_enqueue_coroutine_t, // Додавання в чергу
zend_async_resume_t, // Відновлення
zend_async_cancel_t, // Скасування
// ... та інші
);zend_async_reactor_register(
char *module,
bool allow_override,
zend_async_reactor_startup_t, // Ініціалізація event loop
zend_async_reactor_shutdown_t, // Завершення event loop
zend_async_reactor_execute_t, // Один тік реактора
zend_async_reactor_loop_alive_t, // Чи є активні події
zend_async_new_socket_event_t, // Створення poll-події
zend_async_new_timer_event_t, // Створення таймера
zend_async_new_signal_event_t, // Підписка на сигнал
// ... та інші
);