Wayland의 전역 객체 제거 과정에서 발생하는 경쟁 상태 문제와 이를 해결하기 위한 `wl_fixes.ack_global_remove` 기반 접근을 설명합니다.
저는 코드를 작성합니다
메뉴 및 위젯
검색:
전역 객체는 Wayland의 핵심 추상화 중 하나입니다. 이는 지원되는 프로토콜을 알리거나, 예를 들어 사용 가능한 출력 장치처럼 반일시적인 객체를 알리는 데 사용됩니다. 컴포지터는 실행 중에 전역 객체를 추가하거나 제거할 수 있습니다. 예를 들어 새 모니터를 사용할 수 있게 되면 출력 전역 객체를 생성할 수 있고, 해당 모니터의 연결이 해제되면 이를 제거할 수 있습니다.
새 전역 객체를 알리는 것에는 문제가 없지만, 전역 제거는 경쟁 상태가 발생할 수 있는 과정이며 상황이 나쁘게 흘러가면 애플리케이션이 크래시합니다. 안타깝게도 출력 제거로 인한 애플리케이션 크래시는 예전에도 꽤 흔했고 지금도 여전히 그렇습니다.
처음부터 살펴보겠습니다. 컴포지터가 새 출력을 감지하면 해당 wl_output 전역 객체를 알릴 수 있습니다. 출력에 관심 있는 클라이언트는 결국 그것에 바인드하고, 이름, 기하 정보 등 출력에 대한 정보를 받게 됩니다.

새 wl_output 알림.
흥미로운 부분은 wl_output 전역을 제거할 때입니다. 이상적으로는 컴포지터가 해당 전역이 제거되었다고 클라이언트에 알린 뒤에는 누구도 그것을 사용하거나 바인드하지 않아야 합니다. 클라이언트가 wl_output 전역 객체에 바인드한 상태였다면 정리 작업만 수행해야 합니다.

wl_output 전역 제거를 처리하는 바람직한 방법.
하지만 wl_output 전역이 알린 직후 곧바로 제거되는 일이 생길 수 있습니다. 같은 시점에 클라이언트가 그 wl_output에 바인드하려고 하면 이제 큰 문제가 생깁니다. 바인드 요청이 마침내 컴포지터 쪽에 도착했을 때는 그 전역이 더 이상 존재하지 않게 되고, 남는 선택지는 프로토콜 오류를 게시해 사실상 클라이언트를 크래시시키는 것뿐입니다.

wl_output 전역 제거 경쟁 상태.
안타깝게도 전송 중인 바인드 요청에 대해서는 할 수 있는 일이 많지 않습니다. 그것들은 반드시 처리되어야 합니다. 만약 주어진 전역이 제거되었다고 클라이언트에 알리되 아직 파괴하지는 않는 방식이 가능하다면, 경쟁 상태를 완화하는 데 도움이 될 것이고 여전히 바인드 요청을 처리할 수 있을 것입니다. 컴포지터가 더 이상 아무도 그 전역에 바인드하지 않을 것임을 알게 된 뒤에야 비로소 완전히 파괴할 수 있습니다. 이것이 경쟁 상태를 수정하려는 첫 번째 시도의 핵심 아이디어입니다.

가장 어려운 부분은 전역을 실제로 언제 파괴할 수 있는지 알아내는 것입니다. 가장 좋은 방법은 클라이언트가 전역 제거를 확인 응답하는 것입니다. 컴포지터가 모든 클라이언트로부터 ack를 받으면 마침내 그 전역을 파괴할 수 있습니다. 하지만 여기에는 문제가 있습니다. wl_registry는 동결되어 있어서 새로운 요청이나 이벤트를 추가할 수 없습니다. 그래서 우회 방법으로 사실상 모든 컴포지터는 제거된 전역 객체를 타이머를 두고 파괴하는 방식을 택했습니다.
그것은 경쟁 상태 완화에 도움이 되었고 실제로 크래시 수가 줄어드는 것을 보았지만, 완전히 사라지지는 않았습니다… Linux에서는 단조 시계가 컴퓨터가 잠자는 동안에도 진행될 수 있습니다. 컴퓨터가 절전에서 깨어난 뒤에는 전역 파괴 타이머가 만료되어 전역이 파괴될 가능성이 큽니다. 이것은 매우 큰 문제입니다. 여전히 그 전역을 보았고 바인드하고 싶어 하는 클라이언트가 있을 수 있지만, 컴퓨터가 절전에 들어가는 등의 이유로 제때 요청을 플러시하지 못했을 수 있기 때문입니다. 그래서 결국 다시 원점으로 돌아온 셈입니다.
첫 번째 시도의 일반적인 아이디어 자체는 타당했습니다. 컴포지터는 마침내 전역을 파괴해도 괜찮은 정확한 시점만 알면 됩니다. Wayland 클라이언트가 전역 객체 제거를 확인 응답할 수 있어야 한다는 점은 절대적으로 중요합니다. 그렇다면 wl_registry 인터페이스가 동결되어 있다는 문제는 어떨까요? 여전히 사실이지만, 이제는 새로운 우회 수단이 있습니다 — wl_fixes 인터페이스입니다. wl_fixes 인터페이스는 wl_registry 인터페이스가 동결되어 있다는 사실을 우회하기 위해 추가되었습니다.
업데이트된 계획에서는 프로토콜의 유일한 작은 변경점이, 클라이언트가 wl_registry.global_remove 이벤트를 받은 뒤 컴포지터에 ack 요청을 보내야 한다는 것입니다.

컴포지터가 모든 클라이언트로부터 ack를 받으면 마침내 안전하게 전역을 파괴할 수 있습니다.
이 설명에 따르면 클라이언트는 해당 전역 객체에 바인드하지 않았더라도 모든 wl_registry.global_remove 이벤트를 반드시 확인 응답해야 한다는 점에 유의하세요. 안타깝게도 이것은 또한 컴포지터가 어떤 클라이언트도 실수로 연결이 끊기게 하지 않으면서 전역 데이터를 정리할 수 있으려면, 모든 클라이언트가 올바르게 동작해야 함을 뜻합니다. 단 하나의 클라이언트라도 전역 제거 이벤트에 ack하지 않으면 컴포지터는 좀비 전역을 축적하기 시작할 수 있습니다.
클라이언트 측 변경은 상당히 사소해야 합니다. 클라이언트가 해야 하는 유일한 일은 wl_registry.global_remove 이벤트를 받았을 때 wl_fixes.ack_global_remove 요청을 호출하는 것뿐입니다.
다양한 클라이언트와 툴킷에 wl_fixes.ack_global_remove 지원을 추가하는 패치는 다음과 같습니다.
libwayland-server는 전역 객체 제거를 돕기 위한 몇 가지 헬퍼를 얻게 되었습니다. 컴포지터는 wl_fixes.ack_global_remove 요청을 wl_fixes_handle_ack_global_remove() 함수를 호출하는 방식으로 구현해야 합니다. 이 함수는 libwayland-server가 제공합니다.
전역을 제거하려면 컴포지터는 “withdrawn” 콜백을 설정한 다음 wl_global_remove() 함수를 호출해야 합니다. 나머지 복잡한 작업은 libwayland-server가 처리합니다. 언제 wl_global_destroy() 함수를 호출해도 안전한지 컴포지터에 알리기 위해 withdrawn 콜백을 호출해 줄 것입니다.
돌이켜보면, 어쩌면 사용 가능한 출력은 전역 객체가 아닌 다른 방식으로 알려졌을 수도 있습니다. 그렇다 하더라도 wl_output은 핵심 Wayland 프로토콜의 몇 가지 결함을 드러냈고, 안타깝게도 그것들은 사소하지 않았으며 다양한 클라이언트 크래시로 이어졌습니다. 이 글에서 설명한 작업을 통해 Wayland 세션이 더욱 신뢰할 수 있게 되고, 예기치 않게 크래시하는 애플리케이션이 더 줄어들기를 바랍니다.
아이디어 제안과 코드 리뷰를 해준 Julian Orth와 Pekka Paalanen에게 깊이 감사드립니다!
2026년 3월 24일에 게시됨작성자 Vlad Zahorodnii카테고리 KDE, Wayland
이메일 주소는 공개되지 않습니다.필수 입력 항목은 * 로 표시됩니다.
댓글 *
이름 *
이메일 *
웹사이트
Δ