Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:36:20.277Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

display_link.rs

462 lines · 18.0 KB · rust
1//! Frame pacing for macOS windows, built on `CVDisplayLink`.
2//!
3//! CVDisplayLink has no safe teardown: `CVDisplayLinkStop` merely flags the
4//! link's io thread to exit "soon" and returns immediately, and there is no
5//! way to learn when (or whether) a final output callback has finished. Two
6//! crash classes came from ignoring this:
7//!
8//! * releasing the link after `stop` let the still-running io thread read the
9//!   freed link's internals (segfault in `CVHWTime::reset`, fixed by leaking
10//!   the link in #32116);
11//! * releasing the dispatch source that the output callback dereferenced via
12//!   its context pointer let a straggler callback read freed memory (segfault
13//!   in `dispatch_source_merge_data`, Sentry issue ZED-7XR).
14//!
15//! Instead of leaking one link and one source per teardown (and teardown
16//! happens on every resize tick, activation, and occlusion change), we keep a
17//! single immortal `CVDisplayLink` per display in a static registry and have
18//! windows subscribe to it:
19//!
20//! * Links are created lazily and never released, so the release race cannot
21//!   occur. The output callback's context is the display id (an integer, not
22//!   a pointer), and the only memory the callback dereferences is the static
23//!   registry, so a straggler callback after `stop` is a harmless no-op.
24//! * Each window owns one dispatch source for the life of the window. On
25//!   window close it is removed from the registry under the lock (making it
26//!   unreachable from the callback), cancelled, and genuinely released.
27//! * A display's link runs iff it has subscribers; windows never start or
28//!   stop links directly, so interleaved starts/stops of windows sharing a
29//!   display cannot conflict.
30//!
31//! One tradeoff of immortal entries: a link created for a given
32//! `CGDirectDisplayID` is reused forever, including after the display is
33//! unplugged and one reappears with the same id, or after mode/refresh-rate
34//! changes. `CVDisplayLink` looks up display timing dynamically, so a cached
35//! link keeps pacing correctly; if that ever proves untrue the fix is to
36//! also refresh entries from a display-reconfiguration callback, not to
37//! release links (which would reintroduce the teardown race).
38//!
39//! Lock ordering: the output callback runs on the link's io thread and takes
40//! the registry lock, possibly while holding CVDisplayLink-internal locks. To
41//! avoid a lock cycle through those (undocumented) internals, we never call a
42//! CoreVideo function while holding the registry lock. Registry mutations and
43//! link start/stop happen on the main thread only, which keeps the `running`
44//! flag and the link's actual state consistent without holding the lock
45//! across the calls.
46//!
47//! `std::sync::Mutex` rather than `parking_lot` is deliberate: on macOS it is
48//! currently backed by `os_unfair_lock`, whose priority donation resolves
49//! inversions between the high-priority io thread and the main thread. (That
50//! is a std implementation detail, not a guarantee; if it changes, the cost
51//! is added latency under contention, not incorrectness.)
52
53use anyhow::Result;
54use core_graphics::display::CGDirectDisplayID;
55use dispatch2::{
56    _dispatch_source_type_data_add, DispatchObject, DispatchQueue, DispatchRetained, DispatchSource,
57};
58use gpui_util::ResultExt;
59use std::{
60    collections::{BTreeMap, btree_map},
61    ffi::c_void,
62    sync::{Mutex, MutexGuard, PoisonError},
63};
64
65static REGISTRY: Mutex<Registry> = Mutex::new(Registry::new());
66
67struct Registry {
68    displays: BTreeMap<CGDirectDisplayID, DisplayEntry>,
69    next_subscriber_id: u64,
70}
71
72impl Registry {
73    const fn new() -> Self {
74        Registry {
75            displays: BTreeMap::new(),
76            next_subscriber_id: 0,
77        }
78    }
79}
80
81struct DisplayEntry {
82    link: sys::DisplayLink,
83    running: bool,
84    subscribers: Vec<(SubscriberId, DispatchRetained<DispatchSource>)>,
85}
86
87// SAFETY: Both fields wrapping raw pointers are refcounted handles to
88// thread-safe objects that are valid on any thread: `sys::DisplayLink` to a
89// CoreVideo object, and each subscriber's `DispatchRetained<DispatchSource>`
90// to a GCD object (which the display's io thread really does use, calling
91// `merge_data` from the output callback). All mutation of the entry itself
92// is serialized by the registry lock.
93unsafe impl Send for DisplayEntry {}
94
95#[derive(Copy, Clone, PartialEq, Eq)]
96struct SubscriberId(u64);
97
98fn lock_registry() -> MutexGuard<'static, Registry> {
99    // Proceeding past poison is safe here (the map's invariants hold after
100    // any partial mutation), and panicking instead would abort the process
101    // when this is reached from the extern "C" output callback.
102    REGISTRY.lock().unwrap_or_else(PoisonError::into_inner)
103}
104
105fn debug_assert_main_thread() {
106    #[cfg(debug_assertions)]
107    {
108        use objc::{class, msg_send, sel, sel_impl};
109        let is_main_thread: objc::runtime::BOOL =
110            unsafe { msg_send![class!(NSThread), isMainThread] };
111        debug_assert!(
112            is_main_thread == objc::runtime::YES,
113            "display link registry mutations must happen on the main thread; \
114             the registry's lock ordering and state consistency depend on it"
115        );
116    }
117}
118
119unsafe extern "C" fn display_link_output_callback(
120    _display_link_out: *mut sys::CVDisplayLink,
121    _current_time: *const sys::CVTimeStamp,
122    _output_time: *const sys::CVTimeStamp,
123    _flags_in: i64,
124    _flags_out: *mut i64,
125    display_id: *mut c_void,
126) -> i32 {
127    let display_id = display_id as usize as CGDirectDisplayID;
128    let registry = lock_registry();
129    if let Some(entry) = registry.displays.get(&display_id) {
130        for (_, frame_requests) in &entry.subscribers {
131            frame_requests.merge_data(1);
132        }
133    }
134    0
135}
136
137fn subscribe(
138    display_id: CGDirectDisplayID,
139    frame_requests: DispatchRetained<DispatchSource>,
140) -> Result<SubscriberId> {
141    debug_assert_main_thread();
142
143    let needs_link = !lock_registry().displays.contains_key(&display_id);
144    let new_link = if needs_link {
145        // Created outside the registry lock; see the lock ordering note above.
146        Some(unsafe {
147            sys::DisplayLink::new(
148                display_id,
149                display_link_output_callback,
150                display_id as usize as *mut c_void,
151            )?
152        })
153    } else {
154        None
155    };
156
157    let (subscriber_id, link_to_start) = {
158        let mut registry = lock_registry();
159        let registry = &mut *registry;
160        let subscriber_id = SubscriberId(registry.next_subscriber_id);
161        registry.next_subscriber_id += 1;
162        let entry = match (registry.displays.entry(display_id), new_link) {
163            // If an entry appeared since the check above, dropping `new_link`
164            // is safe: a never-started link has no io thread, so releasing it
165            // doesn't race.
166            (btree_map::Entry::Occupied(entry), _) => entry.into_mut(),
167            (btree_map::Entry::Vacant(vacant), Some(link)) => vacant.insert(DisplayEntry {
168                link,
169                running: false,
170                subscribers: Vec::new(),
171            }),
172            (btree_map::Entry::Vacant(_), None) => {
173                // Entries are never removed, so an entry observed above cannot
174                // have disappeared while subscriptions stay on the main thread.
175                anyhow::bail!("display link registry entry vanished for display {display_id}");
176            }
177        };
178        entry.subscribers.push((subscriber_id, frame_requests));
179        let link_to_start = if entry.running {
180            None
181        } else {
182            entry.running = true;
183            // Clone the refcounted handle so the CVDisplayLinkStart call can
184            // happen after the lock is released.
185            Some(entry.link.clone())
186        };
187        (subscriber_id, link_to_start)
188    };
189
190    if let Some(mut link) = link_to_start {
191        if let Err(error) = unsafe { link.start() } {
192            let mut registry = lock_registry();
193            if let Some(entry) = registry.displays.get_mut(&display_id) {
194                entry.running = false;
195                entry.subscribers.retain(|(id, _)| *id != subscriber_id);
196            }
197            return Err(error);
198        }
199    }
200
201    Ok(subscriber_id)
202}
203
204fn unsubscribe(display_id: CGDirectDisplayID, subscriber_id: SubscriberId) {
205    debug_assert_main_thread();
206
207    let link_to_stop = {
208        let mut registry = lock_registry();
209        let Some(entry) = registry.displays.get_mut(&display_id) else {
210            return;
211        };
212        entry.subscribers.retain(|(id, _)| *id != subscriber_id);
213        if entry.subscribers.is_empty() && entry.running {
214            entry.running = false;
215            Some(entry.link.clone())
216        } else {
217            None
218        }
219    };
220
221    if let Some(mut link) = link_to_stop {
222        // A final output callback can still fire after this returns; it finds
223        // no subscribers for this display and does nothing.
224        unsafe { link.stop().log_err() };
225    }
226}
227
228/// A per-window source of frame requests, paced by the display the window is
229/// on. The wrapped dispatch source coalesces vsync ticks from the display's
230/// io thread and invokes `callback(data)` on the main queue.
231pub struct WindowFrameSource {
232    frame_requests: DispatchRetained<DispatchSource>,
233    registration: Option<(CGDirectDisplayID, SubscriberId)>,
234}
235
236impl WindowFrameSource {
237    pub fn new(data: *mut c_void, callback: extern "C" fn(*mut c_void)) -> Self {
238        let frame_requests = unsafe {
239            let frame_requests = DispatchSource::new(
240                &raw const _dispatch_source_type_data_add as *mut _,
241                0,
242                0,
243                Some(DispatchQueue::main()),
244            );
245            frame_requests.set_context(data);
246            frame_requests.set_event_handler_f(callback);
247            // Resume before this source can ever be dropped: destroying a
248            // suspended dispatch source is undefined behavior (#50875).
249            frame_requests.resume();
250            frame_requests
251        };
252        Self {
253            frame_requests,
254            registration: None,
255        }
256    }
257
258    pub fn start(&mut self, display_id: CGDirectDisplayID) -> Result<()> {
259        self.stop();
260        let subscriber_id = subscribe(display_id, self.frame_requests.clone())?;
261        self.registration = Some((display_id, subscriber_id));
262        Ok(())
263    }
264
265    pub fn stop(&mut self) {
266        if let Some((display_id, subscriber_id)) = self.registration.take() {
267            unsubscribe(display_id, subscriber_id);
268        }
269    }
270}
271
272impl Drop for WindowFrameSource {
273    fn drop(&mut self) {
274        self.stop();
275        // Unsubscribing makes this source unreachable from the output
276        // callback, so unlike before (ZED-7XR) it is safe to actually release
277        // it. Cancelling first guarantees the event handler never runs again;
278        // its context points at the window's native view, which may be
279        // deallocated after this.
280        self.frame_requests.cancel();
281    }
282}
283
284mod sys {
285    //! Derived from display-link crate under the following license:
286    //! <https://github.com/BrainiumLLC/display-link/blob/master/LICENSE-MIT>
287    //! Apple docs: [CVDisplayLink](https://developer.apple.com/documentation/corevideo/cvdisplaylinkoutputcallback?language=objc)
288    #![allow(dead_code, non_upper_case_globals)]
289
290    use anyhow::Result;
291    use core_graphics::display::CGDirectDisplayID;
292    use foreign_types::{ForeignType, foreign_type};
293    use std::{
294        ffi::c_void,
295        fmt::{self, Debug, Formatter},
296    };
297
298    #[derive(Debug)]
299    pub enum CVDisplayLink {}
300
301    foreign_type! {
302        pub unsafe type DisplayLink {
303            type CType = CVDisplayLink;
304            fn drop = CVDisplayLinkRelease;
305            fn clone = CVDisplayLinkRetain;
306        }
307    }
308
309    impl Debug for DisplayLink {
310        fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
311            formatter
312                .debug_tuple("DisplayLink")
313                .field(&self.as_ptr())
314                .finish()
315        }
316    }
317
318    #[repr(C)]
319    #[derive(Clone, Copy)]
320    pub(crate) struct CVTimeStamp {
321        pub version: u32,
322        pub video_time_scale: i32,
323        pub video_time: i64,
324        pub host_time: u64,
325        pub rate_scalar: f64,
326        pub video_refresh_period: i64,
327        pub smpte_time: CVSMPTETime,
328        pub flags: u64,
329        pub reserved: u64,
330    }
331
332    pub type CVTimeStampFlags = u64;
333
334    pub const kCVTimeStampVideoTimeValid: CVTimeStampFlags = 1 << 0;
335    pub const kCVTimeStampHostTimeValid: CVTimeStampFlags = 1 << 1;
336    pub const kCVTimeStampSMPTETimeValid: CVTimeStampFlags = 1 << 2;
337    pub const kCVTimeStampVideoRefreshPeriodValid: CVTimeStampFlags = 1 << 3;
338    pub const kCVTimeStampRateScalarValid: CVTimeStampFlags = 1 << 4;
339    pub const kCVTimeStampTopField: CVTimeStampFlags = 1 << 16;
340    pub const kCVTimeStampBottomField: CVTimeStampFlags = 1 << 17;
341    pub const kCVTimeStampVideoHostTimeValid: CVTimeStampFlags =
342        kCVTimeStampVideoTimeValid | kCVTimeStampHostTimeValid;
343    pub const kCVTimeStampIsInterlaced: CVTimeStampFlags =
344        kCVTimeStampTopField | kCVTimeStampBottomField;
345
346    #[repr(C)]
347    #[derive(Clone, Copy, Default)]
348    pub(crate) struct CVSMPTETime {
349        pub subframes: i16,
350        pub subframe_divisor: i16,
351        pub counter: u32,
352        pub time_type: u32,
353        pub flags: u32,
354        pub hours: i16,
355        pub minutes: i16,
356        pub seconds: i16,
357        pub frames: i16,
358    }
359
360    pub type CVSMPTETimeType = u32;
361
362    pub const kCVSMPTETimeType24: CVSMPTETimeType = 0;
363    pub const kCVSMPTETimeType25: CVSMPTETimeType = 1;
364    pub const kCVSMPTETimeType30Drop: CVSMPTETimeType = 2;
365    pub const kCVSMPTETimeType30: CVSMPTETimeType = 3;
366    pub const kCVSMPTETimeType2997: CVSMPTETimeType = 4;
367    pub const kCVSMPTETimeType2997Drop: CVSMPTETimeType = 5;
368    pub const kCVSMPTETimeType60: CVSMPTETimeType = 6;
369    pub const kCVSMPTETimeType5994: CVSMPTETimeType = 7;
370
371    pub type CVSMPTETimeFlags = u32;
372
373    pub const kCVSMPTETimeValid: CVSMPTETimeFlags = 1 << 0;
374    pub const kCVSMPTETimeRunning: CVSMPTETimeFlags = 1 << 1;
375
376    pub type CVDisplayLinkOutputCallback = unsafe extern "C" fn(
377        display_link_out: *mut CVDisplayLink,
378        // A pointer to the current timestamp. This represents the timestamp when the callback is called.
379        current_time: *const CVTimeStamp,
380        // A pointer to the output timestamp. This represents the timestamp for when the frame will be displayed.
381        output_time: *const CVTimeStamp,
382        // Unused
383        flags_in: i64,
384        // Unused
385        flags_out: *mut i64,
386        // A pointer to app-defined data.
387        display_link_context: *mut c_void,
388    ) -> i32;
389
390    #[link(name = "CoreFoundation", kind = "framework")]
391    #[link(name = "CoreVideo", kind = "framework")]
392    #[allow(improper_ctypes, unknown_lints, clippy::duplicated_attributes)]
393    unsafe extern "C" {
394        pub fn CVDisplayLinkCreateWithActiveCGDisplays(
395            display_link_out: *mut *mut CVDisplayLink,
396        ) -> i32;
397        pub fn CVDisplayLinkSetCurrentCGDisplay(
398            display_link: &mut DisplayLinkRef,
399            display_id: u32,
400        ) -> i32;
401        pub fn CVDisplayLinkSetOutputCallback(
402            display_link: &mut DisplayLinkRef,
403            callback: CVDisplayLinkOutputCallback,
404            user_info: *mut c_void,
405        ) -> i32;
406        pub fn CVDisplayLinkStart(display_link: &mut DisplayLinkRef) -> i32;
407        pub fn CVDisplayLinkStop(display_link: &mut DisplayLinkRef) -> i32;
408        pub fn CVDisplayLinkRelease(display_link: *mut CVDisplayLink);
409        pub fn CVDisplayLinkRetain(display_link: *mut CVDisplayLink) -> *mut CVDisplayLink;
410    }
411
412    impl DisplayLink {
413        /// Apple docs: [CVDisplayLinkCreateWithCGDisplay](https://developer.apple.com/documentation/corevideo/1456981-cvdisplaylinkcreatewithcgdisplay?language=objc)
414        pub unsafe fn new(
415            display_id: CGDirectDisplayID,
416            callback: CVDisplayLinkOutputCallback,
417            user_info: *mut c_void,
418        ) -> Result<Self> {
419            unsafe {
420                let mut display_link: *mut CVDisplayLink = 0 as _;
421
422                let code = CVDisplayLinkCreateWithActiveCGDisplays(&mut display_link);
423                anyhow::ensure!(code == 0, "could not create display link, code: {}", code);
424
425                let mut display_link = DisplayLink::from_ptr(display_link);
426
427                let code = CVDisplayLinkSetOutputCallback(&mut display_link, callback, user_info);
428                anyhow::ensure!(code == 0, "could not set output callback, code: {}", code);
429
430                let code = CVDisplayLinkSetCurrentCGDisplay(&mut display_link, display_id);
431                anyhow::ensure!(
432                    code == 0,
433                    "could not assign display to display link, code: {}",
434                    code
435                );
436
437                Ok(display_link)
438            }
439        }
440    }
441
442    impl DisplayLinkRef {
443        /// Apple docs: [CVDisplayLinkStart](https://developer.apple.com/documentation/corevideo/1457193-cvdisplaylinkstart?language=objc)
444        pub unsafe fn start(&mut self) -> Result<()> {
445            unsafe {
446                let code = CVDisplayLinkStart(self);
447                anyhow::ensure!(code == 0, "could not start display link, code: {}", code);
448                Ok(())
449            }
450        }
451
452        /// Apple docs: [CVDisplayLinkStop](https://developer.apple.com/documentation/corevideo/1457281-cvdisplaylinkstop?language=objc)
453        pub unsafe fn stop(&mut self) -> Result<()> {
454            unsafe {
455                let code = CVDisplayLinkStop(self);
456                anyhow::ensure!(code == 0, "could not stop display link, code: {}", code);
457                Ok(())
458            }
459        }
460    }
461}
462
Served at tenant.openagents/omega Member data and write actions are omitted.