기술 · 실용 가이드
Python “resource_tracker: leaked semaphore objects” 경고? 숨기기 전에 소유자를 찾으세요
종료 경고는 cleanup 증상이지 근본 원인이 아닙니다. 첫 예외를 보존하고 각 process의 semaphore 소유자와 close, join, unlink 순서를 증명합니다.

resource tracker는 interpreter shutdown 때 등록 이름이 남아 있으면 경고한다. 어떤 library가 만들었는지, 마지막 줄이 근본 원인인지는 알려 주지 않는다. signal로 죽은 child, initializer 실패, 닫히지 않은 pool, queue, 이중 unlink가 같은 경고를 낼 수 있다.
첫 exception과 owner 목록에서 시작한다. Python build, start method, constructor, child exit, parent의 close, shutdown, join, unlink를 기록하고 lifecycle이 올바른지 증명한 뒤 suppression을 판단한다.
첫 failure를 보존
stderr와 exit status를 보존하고 tracker 줄 앞의 traceback, signal, initializer failure, timeout, cancel, forced termination을 찾는다.
Python build, OS, start method, parent/child PID, library version과 exit class를 기록하고 UserWarning부터 숨기지 않는다.
constructor와 owner 목록화
Pool, Process, Queue, Lock, Semaphore, ProcessPoolExecutor, SharedMemory, joblib, data-loader worker를 찾아 생성 process와 cleanup scope를 배정한다.
필요한 플랫폼에서는 main guard를 쓰지만 import-time resource나 initializer failure까지 해결하는 만능책으로 보지 않는다.
정상 종료 먼저 증명
context manager를 쓰거나 Pool close/join, executor shutdown, Queue close와 필요한 feeder-thread join을 수행한다. SharedMemory는 합의된 한 owner가 한 번 unlink한다.
baseline에서는 terminate, kill, os._exit, container stop, test timeout을 피한다. forced termination은 shared resource를 남길 수 있다.
실제 leak과 tracker bookkeeping 분리
constructor 하나와 child 하나로 축소하고 create, child exit, close, join, unlink, shutdown 순서를 기록한다. 경고가 사라지면 library layer를 하나씩 되돌린다.
Linux /dev/shm은 보조 evidence일 뿐 삭제 목록이 아니다. 종료된 owner와 연결할 수 없는 이름을 삭제하지 않는다.
지원 버전과 race 테스트
lifecycle이 깨끗하면 현재 지원 patch에서 minimal reproducer를 시험한다. initializer failure, reentrant cleanup, double unlink race는 비슷한 경고를 만들 수 있다.
private unregister로 조용히 하지 말고 double cleanup, missing cleanup, version bug 중 무엇인지 증명한다.
경고 없는 closure test
모든 child exit, parent close/join, 단일 unlink owner, warning 없는 shutdown을 확인하고 원래 service manager 또는 CI cancel path에서도 재시험한다.
알려진 harmless third-party warning을 좁게 filter해도 version, reproducer, upstream issue, removal condition을 기록한다.
