코루틴 대기 및 깨우기 메커니즘
코루틴의 대기 컨텍스트를 저장하기 위해
TrueAsync는 Waker 구조체를 사용합니다.
이는 코루틴과 구독한 이벤트 사이의 연결 고리 역할을 합니다.
Waker 덕분에 코루틴은 항상 어떤 이벤트를 기다리고 있는지 정확히 알 수 있습니다.
Waker 구조체
메모리 최적화를 위해 waker는 코루틴 구조체(zend_coroutine_t)에 직접 통합되어 있으며,
이를 통해 추가 할당을 피하고 메모리 관리를 단순화합니다.
다만 하위 호환성을 위해 코드에서는 zend_async_waker_t *waker 포인터가 사용됩니다.
Waker는 대기 중인 이벤트 목록을 보유하고 대기 결과 또는 예외를 집계합니다.
struct _zend_async_waker_s {
ZEND_ASYNC_WAKER_STATUS status;
// 코루틴이 대기 중인 이벤트
HashTable events;
// 마지막 반복에서 발생한 이벤트
HashTable *triggered_events;
// 깨우기 결과
zval result;
// 오류 (깨우기가 오류로 인한 경우)
zend_object *error;
// 생성 지점 (디버깅용)
zend_string *filename;
uint32_t lineno;
// 소멸자
zend_async_waker_dtor dtor;
};
Waker 상태
코루틴 생명의 각 단계에서 Waker는 다섯 가지 상태 중 하나에 있습니다:
typedef enum {
ZEND_ASYNC_WAKER_NO_STATUS, // Waker가 활성화되지 않음
ZEND_ASYNC_WAKER_WAITING, // 코루틴이 이벤트를 대기 중
ZEND_ASYNC_WAKER_QUEUED, // 코루틴이 실행 대기열에 있음
ZEND_ASYNC_WAKER_IGNORED, // 코루틴이 건너뛰어짐
ZEND_ASYNC_WAKER_RESULT // 결과가 사용 가능
} ZEND_ASYNC_WAKER_STATUS;
코루틴은 NO_STATUS로 시작합니다 – Waker가 존재하지만 활성화되지 않았으며, 코루틴이 실행 중입니다.
코루틴이 SUSPEND()를 호출하면, Waker는 WAITING으로 전환되어 이벤트를 모니터링하기 시작합니다.
이벤트 중 하나가 발생하면, Waker는 QUEUED로 전환됩니다: 결과가 저장되고,
코루틴이 컨텍스트 전환을 대기하며 Scheduler 큐에 배치됩니다.
IGNORED 상태는 코루틴이 이미 큐에 있지만 파괴되어야 하는 경우에 필요합니다.
이 경우 Scheduler는 코루틴을 시작하지 않고 즉시 상태를 마무리합니다.
코루틴이 깨어나면 Waker는 RESULT 상태로 전환됩니다.
이 시점에서 waker->error가 EG(exception)으로 전달됩니다.
오류가 없으면 코루틴은 waker->result를 사용할 수 있습니다. 예를 들어, result는
await() 함수가 반환하는 값입니다.
Waker 생성
// waker 가져오기 (존재하지 않으면 생성)
zend_async_waker_t *waker = zend_async_waker_define(coroutine);
// 새 대기를 위해 waker 재초기화
zend_async_waker_t *waker = zend_async_waker_new(coroutine);
// 타임아웃 및 취소와 함께
zend_async_waker_t *waker = zend_async_waker_new_with_timeout(
coroutine, timeout_ms, cancellation_event);
zend_async_waker_new()는 기존 waker를 소멸시키고
초기 상태로 재설정합니다. 이를 통해 할당 없이
waker를 재사용할 수 있습니다.
이벤트 구독
zend_async_API.c 모듈은 코루틴을 이벤트에 바인딩하는 여러 기성 함수를 제공합니다:
zend_async_resume_when(
coroutine, // 깨울 코루틴
event, // 구독할 이벤트
trans_event, // 이벤트 소유권 이전
callback, // 콜백 함수
event_callback // 코루틴 콜백 (또는 NULL)
);
resume_when은 주요 구독 함수입니다.
zend_coroutine_event_callback_t를 생성하여 이벤트와
코루틴의 waker에 바인딩합니다.
콜백 함수로는 코루틴을 깨우는 방법에 따라 세 가지 표준 함수 중 하나를 사용할 수 있습니다:
// 성공적인 결과
zend_async_waker_callback_resolve(event, callback, result, exception);
// 취소
zend_async_waker_callback_cancel(event, callback, result, exception);
// 타임아웃
zend_async_waker_callback_timeout(event, callback, result, exception);