diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-28 23:29:11 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-28 23:29:11 +0300 |
| commit | e27a4ea427c9ed6248c0ee80570e94ed7b1928bd (patch) | |
| tree | 0cae21a2b3939293be129aaaecb23641f68bab54 /src/render_signal.rs | |
| parent | e16d7d8c07a20f5ee0b4111808680bbcfd7df9ac (diff) | |
perf: skip rendering hidden pty updates
Diffstat (limited to 'src/render_signal.rs')
| -rw-r--r-- | src/render_signal.rs | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/src/render_signal.rs b/src/render_signal.rs new file mode 100644 index 00000000..d4544e5f --- /dev/null +++ b/src/render_signal.rs @@ -0,0 +1,91 @@ +use std::collections::HashSet; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; + +use crate::layout::PaneId; + +#[derive(Debug, Default)] +pub(crate) struct RenderRequest { + pub(crate) generic: bool, + pub(crate) pty_sources: HashSet<PaneId>, +} + +/// Coalesces render requests while retaining enough origin information for the +/// headless server to discard PTY-only updates hidden from every client. +#[derive(Debug, Default)] +pub(crate) struct RenderSignal { + pending: AtomicBool, + request: Mutex<RenderRequest>, +} + +impl RenderSignal { + pub(crate) fn new() -> Self { + Self::default() + } + + pub(crate) fn is_pending(&self) -> bool { + self.pending.load(Ordering::Acquire) + } + + pub(crate) fn request_generic(&self) { + let mut request = self + .request + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + request.generic = true; + self.pending.store(true, Ordering::Release); + } + + /// Returns true when this request transitions the signal from idle to pending. + pub(crate) fn request_pty(&self, pane_id: PaneId) -> bool { + let mut request = self + .request + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + request.pty_sources.insert(pane_id); + !self.pending.swap(true, Ordering::AcqRel) + } + + pub(crate) fn take(&self) -> RenderRequest { + let mut request = self + .request + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + self.pending.store(false, Ordering::Release); + std::mem::take(&mut *request) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn coalesces_pty_sources_until_taken() { + let signal = RenderSignal::new(); + let first = PaneId::from_raw(10); + let second = PaneId::from_raw(20); + + assert!(signal.request_pty(first)); + assert!(!signal.request_pty(first)); + assert!(!signal.request_pty(second)); + + let request = signal.take(); + assert!(!request.generic); + assert_eq!(request.pty_sources, HashSet::from([first, second])); + assert!(!signal.is_pending()); + } + + #[test] + fn keeps_generic_and_pty_requests_distinct() { + let signal = RenderSignal::new(); + let pane_id = PaneId::from_raw(10); + + signal.request_generic(); + assert!(!signal.request_pty(pane_id)); + + let request = signal.take(); + assert!(request.generic); + assert_eq!(request.pty_sources, HashSet::from([pane_id])); + } +} |
