← All reports

`WebEvent` and subclasses converted to ref-counted heap objects

Component: WebKit input events | 70826d7

WebEvent와 그 서브클래스는 WebKit이 다루는 mouse, keyboard, touch, wheel, gesture 이벤트를 모두 표현합니다. 이 이벤트들은 플랫폼과 UIProcess에서 출발해 IPC를 거쳐 WebProcess로 넘어가고, 최종적으로 DOM에 dispatch됩니다. 한편 CheckedPtr / CanMakeCheckedPtr는 WebKit이 사용하는 가벼운 lifetime 검사용 포인터입니다. 객체가 실제로 소멸되기 전에 일반적인 operator delete가 호출되었는지를 assert로 확인하며, 이를 통해 dangling pointer 오용을 잡아냅니다.

이번 commit은 WebEvent와 모든 구체 서브클래스의 성격 자체를 바꿨습니다. 기존에는 스택에 올릴 수 있고 이동 가능한 value type이었지만, 이제는 항상 heap에 할당되는 ref-counted 객체가 되었습니다. 생성은 Ref<> / RefPtr<>를 반환하는 static create() factory를 거칩니다. 이벤트별 필드는 평범한 WebEventData / WebXEventData struct로 빠져나갔는데, 덕분에 IPC wire format은 바이트 단위로 동일하게 유지됩니다. 또한 각 구체 클래스마다 별도의 WTF_MAKE_TZONE_ALLOCATED가 붙었습니다. 계층 전체는 단순한 RefCounted 대신 ThreadSafeRefCounted를 사용합니다. EventDispatcher가 wheel과 touch 이벤트를 main thread로 넘기기 전에 IPC background queue에서 먼저 처리하기 때문입니다.

WebEventCanMakeThreadSafeCheckedPtr를 적용해 두면서도, 스택 할당이 일상적으로 이루어진다는 이유로 delete-check 예외를 함께 걸어 두었습니다. 그 결과 브라우저에서 가장 빈번하게 오가는 프로세스 간 데이터 경로 중 하나에서 lifetime assertion이 한 번도 동작할 수 없었습니다. 이번 변경으로 그 공백이 메워졌습니다. 다만 대가는 변경 범위입니다. 모든 플랫폼의 이벤트 생성, coalescing, dispatch 코드에 걸쳐 수십 개 파일이 수정되었는데, 이 범위 자체가 새로운 lifetime 버그나 copy semantics 버그가 유입될 수 있는 넓은 표면에 해당합니다.

앞으로 눈여겨볼 패턴은 이렇습니다. 어떤 클래스가 lifetime 검사 메커니즘을 도입해 놓고, 동시에 그 검사를 무의미하게 만드는 예외를 함께 선언하는 경우입니다. CanMakeCheckedPtr / CanMakeThreadSafeCheckedPtr를 사용하는 다른 클래스 가운데, opt-in과 delete-check 예외를 나란히 선언한 곳을 검색해 볼 필요가 있습니다. 그런 클래스는 실제로는 갖고 있지 않은 보호를 갖춘 것처럼 내세우는 셈입니다. 코드 리뷰에서 이를 식별하는 단서는 단순합니다. 같은 클래스 안에서 예외 매크로가 opt-in 바로 옆에 붙어 있는 형태입니다. 한편 이번 변환 자체가 모든 플랫폼의 이벤트 경로에서 copy semantics를 바꿔 놓았습니다. 특히 coalescing 코드는 이전까지 자유롭게 복사하고 변경할 수 있는 value type을 다뤄 왔습니다. 그래서 aliasing 버그가 자리 잡기에 가장 자연스러운 지점에 해당합니다.