가상 쓰레드 스케줄러 — ForkJoinPool · Work-Stealing · Poller

3.5초/틱
VT (I/O 작업)
VT (CPU 작업)
Carrier (OS Thread)
Parked VT
훔치기 / I/O 이벤트
Poller Thread
활성 캐리어 0 / 4
실행 대기 VT 0
Parked VT 0
완료된 VT 0
총 훔치기 0

① Poller Thread — epoll_wait()에서 잠들어 있다가 이벤트 도착

📡 Poller
epoll_wait 대기 중
도착한 I/O 완료 이벤트 (커널 → Poller)

② ForkJoinPool — Carrier × Chase-Lev Deque (LIFO pop / FIFO steal)

③ Parked VTs — JVM Heap에 보관 (I/O 대기 중인 Continuation)

실행 로그

📖 이 시뮬레이션이 보여주는 것

1. ForkJoinPool & 워커별 deque: 4개의 Carrier(OS 쓰레드)가 각자 자기 WorkQueue를 가집니다. 일반 ThreadPoolExecutor처럼 공유 큐가 아니에요.

2. Chase-Lev deque의 양방향: 주인 워커는 top에서 push/pop (LIFO — 캐시 친화적). 다른 워커가 일 없으면 base에서 steal (FIFO — 충돌 최소).

3. 작업 흐름: Carrier가 자기 deque의 top에서 VT를 꺼내 mount → 람다 실행 → I/O 만나면 LockSupport.park() → Continuation을 힙에 저장하고 unmount → Carrier는 즉시 다음 VT 처리.

4. Poller의 역할: 별도 OS 쓰레드가 epoll_wait()에서 잠들어 있음. 커널이 I/O 완료 이벤트를 알려주면 깨어나서 해당 VT를 unpark → 어느 carrier의 deque에 다시 enqueue → 누군가 mount해서 이어 실행.

해보세요: "VT 10개 폭주"로 한쪽 carrier에 작업을 몰아 보내면 다른 carrier가 work-stealing을 시작합니다. 또 "I/O 완료 트리거"로 parked VT가 어떻게 deque로 복귀하는지 보세요.