CRuby 스레드 스케줄러와 인터럽트
CRuby 스레드 스케줄러는 스레드 실행의 효율성과 공정성을 위해 다양한 기능을 수행합니다.
-
시간 분할(Time sharing): 기본적으로 각 스레드는 약 100ms의 CPU 런타임을 가집니다.
-
블로킹 작업(Blocking operations): I/O나
sleep과 같은 블로킹 작업 시 다른 스레드가 실행됩니다. -
우선순위(Priority): 스레드 우선순위는 스케줄러가 다음 실행 스레드를 선택하고 실행 시간을 할당하는 데 영향을 미칩니다.
-
제어 전달(Passing control): 스레드는 스케줄러에게 제어를 넘기거나 중지할 것을 제안할 수 있습니다.
-
잠금(Locking): 자원 접근 동기화를 위해 스케줄러가 접근 순서를 결정합니다.
비트마스크를 통한 인터럽트 관리
CRuby는 내부적으로 “인터럽트” 개념을 사용하여 스레드 스케줄링 동작을 구현합니다. 이는 정수 마스크로 표현되며, 각 비트는 특정 인터럽트 이벤트를 나타냅니다.
-
TIMER_INTERRUPT_MASK(0x01) -
PENDING_INTERRUPT_MASK(0x02) -
POSTPONED_JOB_INTERRUPT_MASK(0x04) -
TRAP_INTERRUPT_MASK(0x08) -
TERMINATE_INTERRUPT_MASK(0x10) -
VM_BARRIER_INTERRUPT_MASK(0x20)
이러한 비트마스크는 단일 숫자 내에서 여러 프로그램 상태를 효율적으로 표현하며, CPU는 비트 연산에 최적화되어 있어 인터럽트 확인이 매우 빠릅니다. RUBY_VM_CHECK_INTS 매크로는 if 문이나 메서드 호출과 같은 VM 명령어 내부의 체크포인트에서 인터럽트를 확인합니다.
rb_threadptr_execute_interrupts 함수
인터럽트가 감지되면 rb_threadptr_execute_interrupts 함수가 호출되어 실제 인터럽트 처리를 수행합니다.
TIMER_INTERRUPT_MASK: 스레드의 할당된 실행 시간을 확인하고, 초과 시rb_thread_schedule_limits를 통해 스레드 컨텍스트 전환(yield)을 유도합니다. `Thread
priority`에 따라 실행 시간이 조정됩니다.
-
TRAP_INTERRUPT_MASK: 시그널 핸들링을 처리하며, Ruby 3.4+부터는 Ractor 내부의require및autoload와 같은 작업을 메인 Ractor 스레드에서 실행하는 데 사용됩니다. -
PENDING_INTERRUPT_MASK: `Thread
raise 또는 Thread
kill과 같은 스레드 간 예외 및 종료 요청을 처리합니다. 스레드의 pending_interrupt_queue`에 저장된 오류를 꺼내 처리하거나 스레드를 종료합니다.
-
TERMINATE_INTERRUPT_MASK: 주로 Ruby 프로세스 종료 시 모든 Ractor의 스레드를 종료하는 데 사용됩니다. -
POSTPONED_JOB_INTERRUPT_MASK: 현재 컨텍스트에서 안전하게 실행할 수 없는 작업을postponed_job_queue에 넣어 나중에 처리합니다. Tracepoint 코드에서 주로 사용됩니다. -
VM_BARRIER_INTERRUPT_MASK: YJIT 컴파일과 같이 VM 전체에 대한 독점적인 접근이 필요한 작업 시, 모든 스레드를 특정 지점에서 대기시키는 VM 배리어를 설정합니다.
interrupt_mask는 특정 인터럽트가 실행되는 것을 일시적으로 차단하는 역할을 하여, `Signal