diff options
Diffstat (limited to 'src/render_signal.rs')
| -rw-r--r-- | src/render_signal.rs | 104 |
1 files changed, 66 insertions, 38 deletions
diff --git a/src/render_signal.rs b/src/render_signal.rs index f4b4391d..a6ebd839 100644 --- a/src/render_signal.rs +++ b/src/render_signal.rs @@ -16,7 +16,13 @@ pub(crate) struct RenderRequest { #[derive(Debug, Default)] pub(crate) struct RenderSignal { pending: AtomicBool, - request: Mutex<RenderRequest>, + state: Mutex<RenderSignalState>, +} + +#[derive(Debug, Default)] +struct RenderSignalState { + request: RenderRequest, + immediate_pty_sources: HashSet<PaneId>, } impl RenderSignal { @@ -29,78 +35,75 @@ impl RenderSignal { } pub(crate) fn request_generic(&self) { - let mut request = self - .request + let mut state = self + .state .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - request.generic = true; + state.request.generic = true; self.pending.store(true, Ordering::Release); } - /// Returns true when the signal becomes pending or a new PTY source joins it. - /// - /// A new source may be visible even when the existing pending sources are - /// hidden, so the consumer must re-evaluate the coalesced request. + /// Returns true when the signal becomes pending or visible PTY work joins it. pub(crate) fn request_pty(&self, pane_id: PaneId) -> bool { - let mut request = self - .request + let mut state = self + .state .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - let source_added = request.pty_sources.insert(pane_id); + let source_added = state.request.pty_sources.insert(pane_id); + let wake_for_source = source_added && state.immediate_pty_sources.contains(&pane_id); let became_pending = !self.pending.swap(true, Ordering::AcqRel); - became_pending || source_added + became_pending || wake_for_source } - pub(crate) fn has_generic_or_terminal_title(&self) -> bool { - let request = self - .request + pub(crate) fn set_immediate_pty_sources(&self, sources: HashSet<PaneId>) { + self.state .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - request.generic || !request.terminal_title_sources.is_empty() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .immediate_pty_sources = sources; } - /// Checks pending PTY origins without allocating a source snapshot. - /// Keep the predicate narrow because producers share this lock. - pub(crate) fn has_pty_source_matching( - &self, - mut predicate: impl FnMut(PaneId) -> bool, - ) -> bool { - self.request + pub(crate) fn has_immediate_work(&self) -> bool { + let state = self + .state .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .pty_sources - .iter() - .copied() - .any(&mut predicate) + .unwrap_or_else(std::sync::PoisonError::into_inner); + state.request.generic + || !state.request.terminal_title_sources.is_empty() + || state + .request + .pty_sources + .iter() + .any(|pane_id| state.immediate_pty_sources.contains(pane_id)) } /// Coalesces terminal-title changes separately from ordinary PTY damage so /// consumers can update metadata without inspecting every pane. pub(crate) fn request_terminal_title(&self, pane_id: PaneId) -> bool { - let mut request = self - .request + let mut state = self + .state .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - let source_added = request.terminal_title_sources.insert(pane_id); + let source_added = state.request.terminal_title_sources.insert(pane_id); let became_pending = !self.pending.swap(true, Ordering::AcqRel); became_pending || source_added } pub(crate) fn pending_terminal_title_sources(&self) -> HashSet<PaneId> { - self.request + self.state .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) + .request .terminal_title_sources .clone() } pub(crate) fn take(&self) -> RenderRequest { - let mut request = self - .request + let mut state = self + .state .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); self.pending.store(false, Ordering::Release); - std::mem::take(&mut *request) + std::mem::take(&mut state.request) } } @@ -116,7 +119,7 @@ mod tests { assert!(signal.request_pty(first)); assert!(!signal.request_pty(first)); - assert!(signal.request_pty(second)); + assert!(!signal.request_pty(second)); let request = signal.take(); assert!(!request.generic); @@ -126,6 +129,31 @@ mod tests { } #[test] + fn hidden_pty_sources_coalesce_to_one_wake() { + let signal = RenderSignal::new(); + signal.set_immediate_pty_sources(HashSet::from([PaneId::from_raw(100)])); + + let wakes = (1..=50) + .filter(|pane_id| signal.request_pty(PaneId::from_raw(*pane_id))) + .count(); + + assert_eq!(wakes, 1); + } + + #[test] + fn immediate_pty_source_wakes_pending_hidden_work() { + let signal = RenderSignal::new(); + let hidden = PaneId::from_raw(10); + let visible = PaneId::from_raw(20); + signal.set_immediate_pty_sources(HashSet::from([visible])); + + assert!(signal.request_pty(hidden)); + assert!(!signal.request_pty(PaneId::from_raw(30))); + assert!(signal.request_pty(visible)); + assert!(!signal.request_pty(visible)); + } + + #[test] fn terminal_title_source_wakes_pending_pty_work() { let signal = RenderSignal::new(); let pane_id = PaneId::from_raw(10); @@ -158,7 +186,7 @@ mod tests { let pane_id = PaneId::from_raw(10); signal.request_generic(); - assert!(signal.request_pty(pane_id)); + assert!(!signal.request_pty(pane_id)); let request = signal.take(); assert!(request.generic); |
