OSSCA Chromium

기여 목록 / 8395074

FSA: Flush pending inotify moves at read boundaries

ContentfixMerged
  • 문제: inotify 읽기 버퍼 끝에 남은 이동 이벤트가 다음 배치 처리 과정에서 누락될 수 있었습니다.
  • 해결: 버퍼가 해제되기 전에 대기 중인 이동 이벤트를 전달하고, 읽기 경계를 제어하는 회귀 테스트를 추가했습니다.
  • 결과: 다음 배치가 준비된 경우에도 이동 이벤트를 보존하며, 코드 리뷰와 CQ를 거쳐 Chromium에 반영되었습니다.

문제 설명

FilePathWatcher의 inotify reader는 IN_MOVED_FROM과 같은 cookie를 가진 IN_MOVED_TO를 같은 읽기 버퍼 안에서 만나면 하나의 이동으로 묶어 처리합니다. 버퍼 끝에서 짝을 찾지 못한 IN_MOVED_FROM은 현재 버퍼 내부를 가리키는 포인터로 잠시 보관합니다.

기존 구현은 poll()로 다음 배치가 즉시 준비되어 있는지 확인한 뒤, 다음 배치가 없을 때만 대기 중인 이벤트를 전달했습니다. 다음 배치가 준비되어 있으면 현재 버퍼가 해제되고 다음 반복에서 대기 포인터가 초기화되어, 이전 버퍼의 마지막 이동 이벤트가 조용히 사라질 수 있었습니다.

이 문제는 CQ 타임아웃을 조사하는 과정에서 발견했습니다. 다만 당시 CQ에서 실제로 어느 지점에 읽기 경계가 생겼는지는 확인되지 않았으므로, 해당 타임아웃의 원인으로 단정하지 않았습니다.

해결 내용

  1. 버퍼 해제 전 대기 이벤트 전달

    file_path_watcher_inotify.cc에서 다음 배치 유무와 관계없이, 현재 버퍼가 유효한 동안 대기 중인 IN_MOVED_FROM을 전달하도록 수정했습니다. 기존 조건과 변경한 조건의 핵심 차이는 다음과 같습니다.

    -if (!has_batch && pending_move_from_event) {
    +if (pending_move_from_event) {

    이동 이벤트를 묶는 범위는 같은 읽기 버퍼 안으로 유지했습니다. 서로 다른 버퍼로 나뉜 이동 이벤트는 cookie가 같아도 별도의 알림으로 전달합니다.

  2. 실제 읽기 루프를 테스트에서 호출하도록 분리

    읽기와 이벤트 분류 로직을 ReadInotifyEvents()로 추출하고, 콜백으로 단일 이벤트 또는 이동 이벤트 쌍을 전달하도록 구성했습니다. ThreadMain()은 이 콜백을 기존 InotifyReader 처리 함수에 연결합니다.

    file_path_watcher_inotify.h에는 ReadInotifyEventsForTesting()을 선언하여 테스트에서도 같은 읽기 루프를 호출하도록 했습니다. 이벤트 포인터는 콜백 실행 중에만 유효하므로 버퍼 수명 밖으로 보관하지 않습니다.

  3. 리뷰를 반영한 오류 처리 순서 유지

    리뷰에서는 대기 이벤트를 전달하는 위치가 poll() 오류 경로의 동작을 바꾸는지 확인했습니다. Patch Set 4에서 이벤트 전달을 poll_result < 0 검사 뒤로 옮겨, 오류가 발생하면 기존처럼 즉시 반환하도록 했습니다. 정상적인 poll_result == 0과 poll_result > 0 경로에서는 모두 대기 이벤트를 전달합니다.

테스트 방법

file_path_watcher_unittest.cc에 Linux와 ChromeOS용 InotifyReaderTest를 추가했습니다. socketpair(AF_UNIX, SOCK_SEQPACKET, ...)로 배치를 미리 큐에 넣어, 파일 시스템이나 스레드 실행 시점에 의존하지 않고 읽기 경계와 다음 배치의 준비 상태를 재현합니다. 테스트는 ReadInotifyEventsForTesting()을 통해 실제 읽기 루프를 실행합니다.

테스트 검증 내용
FlushesMoveFromWhenAnotherBatchIsReady 첫 배치의 IN_MOVED_FROM과 다음 배치의 IN_CREATE가 모두 순서대로 전달되는지 확인합니다.
PairsMovesWithinSameBuffer 같은 버퍼 안에서 cookie가 일치하는 이동 이벤트가 하나의 쌍으로 처리되는지 확인합니다.
DoesNotPairMovesAcrossBuffers 서로 다른 버퍼의 이동 이벤트는 cookie가 같아도 두 개의 개별 알림으로 전달되는지 확인합니다.

Gerrit 기록상 Patch Set 4는 2026년 9월 16일 UTC에 CQ dry run을 통과했습니다. 이후 full run에서 win-arm64-compile-dbg의 WebUI 헤더 의존성 검사 실패가 기록되었으며, 재실행 후 9월 21일 23:57 UTC(한국 시간 9월 22일 08:57)에 최종 병합되었습니다.

최종 커밋은 2591a644d878a7c24c69cceb6ef3b06646892b8f이며, 커밋 위치는 refs/heads/main@{#1702219}입니다. 위 테스트와 CQ 결과는 패치 및 Gerrit 기록을 기준으로 정리했으며, 보고서 작성 과정에서 Chromium 테스트를 별도로 실행하지는 않았습니다.

배운 점

  • 버퍼 내부를 가리키는 포인터로 이벤트를 보관할 때는 다음 반복으로 넘어가기 전에 버퍼 수명과 미처리 이벤트를 함께 확인해야 합니다.
  • 읽기 경계가 원인인 문제는 패킷 소켓으로 경계를 고정하면 실행 타이밍에 의존하지 않는 회귀 테스트로 재현할 수 있습니다.
  • 이벤트 누락을 수정할 때도 기존의 이동 이벤트 결합 범위와 오류 처리 순서를 유지해야 합니다.
  • 조사 중 발견한 결함과 최초 장애의 원인은 구분해야 하며, 재현되지 않은 인과관계는 커밋 설명에서 단정하지 않아야 합니다.

참고 자료