Skip to repository content462 lines · 18.0 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:36:20.277Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
display_link.rs
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