direct-bind 젬의 필요성 및 문제 해결
루비 확장 개발 시, rb_thread_alive_p와 같이 readelf로 LOCAL로 분류되는 내부 C 함수에 직접 접근해야 하는 경우가 발생합니다. 이 함수들은 `Thread
alive?`와 같은 루비 메서드에 바인딩되어 있지만, 특정 상황에서는 루비 메서드 호출이 불가능하거나 위험합니다.
기존 접근 방식의 한계
-
불안정한 VM 상태: 가비지 컬렉션 중이거나 스레드 종료 과정, 또는 전역 VM 락(GVL)이 없는 상황에서는 루비 메서드 호출이 VM 충돌을 유발할 수 있습니다.
-
실제 사례:
gvl-tracing젬 개발 중 스레드 종료 시 `Thread
alive?` 호출이 충돌을 일으켰습니다. 이는 루비가 이미 스레드의 일부 상태를 정리하여 메서드 호출이 불가능해졌기 때문입니다.
- 대안의 실패: 파이버(fiber) 생존 여부 확인 API도 파이버 객체 지연 할당 문제로 인해 스레드 종료 시 할당이 불가능하여 실패했습니다.
direct-bind 젬의 해결책
direct-bind 젬은 이러한 문제를 해결하기 위해 고안되었습니다. 이 젬은 다음과 같은 원리를 활용합니다.
-
객체 힙 탐색:
rb_objspace_each_objectsAPI를 사용하여 루비 객체 힙을 순회하며 원하는 객체를 찾습니다. -
내부 데이터 구조 활용: 루비의 내부 데이터 구조 레이아웃이 오랫동안 안정적으로 유지되어,
rb_define_method에 의해 바인딩된 C 함수의 포인터를 직접 획득할 수 있습니다.
direct-bind 사용 예시
c
VALUE (*is_thread_alive)(VALUE thread);
is_thread_alive = direct_bind_get_cfunc_with_arity(rb_cThread, rb_intern("alive?"), 0, true).func;
is_thread_alive(rb_thread_current());
위 예시처럼 direct-bind를 통해 `Thread