From 92f992e2e6464a9b2fa286fa64f14467062c628f Mon Sep 17 00:00:00 2001 From: facile pop culture reference Date: Sat, 29 Aug 2026 18:45:46 +0300 Subject: [PATCH 1/2] extract deps; add parking-lot, tracing-tracy --- Cargo.lock | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 ++ src/deps.rs | 29 ++++++++++++++++++ src/lib.rs | 23 +-------------- 4 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 src/deps.rs diff --git a/Cargo.lock b/Cargo.lock index 1acd8b9..c0116f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -797,6 +797,21 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b854b0e584ead1a33f18b2fcad7cf7be18b3875c78816b753639aa501513ae" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "windows-result", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1178,6 +1193,19 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lru" version = "0.18.2" @@ -1197,6 +1225,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.8.3" @@ -2615,6 +2652,7 @@ dependencies = [ "konst", "midly", "palette", + "parking_lot", "profiling", "proptest", "proptest-derive", @@ -2625,6 +2663,7 @@ dependencies = [ "tracing", "tracing-flame", "tracing-subscriber", + "tracing-tracy", "unicode-width", "winit", ] @@ -2890,14 +2929,50 @@ version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex-automata", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] +[[package]] +name = "tracing-tracy" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracy-client", +] + +[[package]] +name = "tracy-client" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f6fc3baeac5d86ab90c772e9e30620fc653bf1864295029921a15ef478e6a5" +dependencies = [ + "loom", + "once_cell", + "tracy-client-sys", +] + +[[package]] +name = "tracy-client-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f7c95348f20c1c913d72157b3c6dee6ea3e30b3d19502c5a7f6d3f160dacbf" +dependencies = [ + "cc", + "windows-targets", +] + [[package]] name = "ttf-parser" version = "0.25.1" @@ -3332,6 +3407,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index fea5807..984508f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ anyhow = { version = "1.0" } atomic_float = { version = "1" } better-panic = { version = "0.3.0" } konst = { version = "0.3.16", features = [ "rust_1_83" ] } +parking_lot = { version = "0.12.5" } bumpalo = { optional = true, version = "3.19.0" } crossterm = { optional = true, version = "0.29.0" } @@ -44,6 +45,8 @@ profiling = { version = "1.0.18", optional = true, features = ["profile tracing = { version = "0.1", optional = true, default-features = false, features = [ "std" ] } tracing-subscriber = { version = "0.3", optional = true } tracing-flame = { version = "0.2", optional = true } +tracing-tracy = { version = "0.11.4", optional = true } + [dev-dependencies] proptest = { version = "^1" } diff --git a/src/deps.rs b/src/deps.rs new file mode 100644 index 0000000..5b3dd7f --- /dev/null +++ b/src/deps.rs @@ -0,0 +1,29 @@ +pub extern crate atomic_float; +pub extern crate palette; +pub extern crate better_panic; +pub extern crate unicode_width; +pub extern crate parking_lot; +pub(crate) use ::{ + std::fmt::{Debug, Display}, + std::ops::{Add, Sub, Mul, Div}, + std::sync::Arc, + std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::*}, + std::marker::PhantomData +}; + +pub(crate) use ::parking_lot::RwLock; + +#[cfg(feature = "lang")] pub use ::dizzle::*; +#[cfg(feature = "prof")] pub extern crate profiling; +#[cfg(feature = "prof")] pub extern crate tracing; +#[cfg(feature = "prof")] pub extern crate tracing_subscriber; +#[cfg(feature = "prof")] pub extern crate tracing_flame; +//#[cfg(feature = "prof")] pub extern crate tracing_tracy; +#[cfg(feature = "sing")] pub extern crate jack; +#[cfg(feature = "midi")] pub extern crate midly; +#[cfg(feature = "term")] pub extern crate ratatui; +#[cfg(feature = "term")] pub extern crate crossterm; +#[cfg(feature = "lang")] pub extern crate dizzle; + +#[cfg(test)] #[macro_use] pub extern crate proptest; +#[cfg(test)] pub(crate) use proptest_derive::Arbitrary; diff --git a/src/lib.rs b/src/lib.rs index aad88f6..e4efc8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,28 +9,7 @@ //#![feature(trait_alias)] //#![feature(type_alias_impl_trait)] //#![feature(type_changing_struct_update)] -pub extern crate atomic_float; -pub extern crate palette; -pub extern crate better_panic; -pub extern crate unicode_width; -#[cfg(feature = "sing")] pub extern crate jack; -#[cfg(feature = "midi")] pub extern crate midly; -#[cfg(feature = "term")] pub extern crate ratatui; -#[cfg(feature = "term")] pub extern crate crossterm; -#[cfg(feature = "lang")] pub extern crate dizzle; -#[cfg(test)] #[macro_use] pub extern crate proptest; -#[cfg(test)] pub(crate) use proptest_derive::Arbitrary; - -pub(crate) use ::{ - std::fmt::{Debug, Display}, - std::ops::{Add, Sub, Mul, Div}, - std::sync::{Arc, RwLock}, - std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::*}, - std::marker::PhantomData -}; - -#[cfg(feature = "lang")] pub use ::dizzle::{Usually, Perhaps}; -#[cfg(feature = "lang")] use ::dizzle::*; +mod deps; pub use self::deps::*; /// Define a trait an implement it for various mutation-enabled wrapper types. */ #[macro_export] macro_rules! flex_trait_mut ( From 8e7286e409ec6d4ac4382856ff93767a4758e11e Mon Sep 17 00:00:00 2001 From: facile pop culture reference Date: Sat, 29 Aug 2026 18:46:35 +0300 Subject: [PATCH 2/2] improve tracing and locks --- src/layout/area.rs | 1 + src/layout/axis.rs | 7 +++ src/layout/azimuth.rs | 14 ++++-- src/layout/cond.rs | 37 ++++++++++++---- src/lib.rs | 99 ++++++++++++++++++++++++++++++------------- src/term.rs | 12 +++--- 6 files changed, 123 insertions(+), 47 deletions(-) diff --git a/src/layout/area.rs b/src/layout/area.rs index 11647eb..89d9e4d 100644 --- a/src/layout/area.rs +++ b/src/layout/area.rs @@ -19,6 +19,7 @@ pub struct Area( impl<'a, S: Screen, T: Draw> Draw for Area { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("area_draw"); to.draw(self.0, &self.1) } } diff --git a/src/layout/axis.rs b/src/layout/axis.rs index 37c98df..0ea9cfa 100644 --- a/src/layout/axis.rs +++ b/src/layout/axis.rs @@ -297,6 +297,7 @@ where impl <'a, S: Screen, I: Draw> Draw for Full<'a, S, I> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("full_draw"); let XYWH(x0, y0, w0, h0) = to.area(); let item = match self { Self::W(i) => i, Self::H(i) => i, Self::WH(i) => i, _ => unreachable!() @@ -315,6 +316,7 @@ impl <'a, S: Screen, I: Draw> Draw for Full<'a, S, I> { impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Exact<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("exact_draw"); let XYWH(x0, y0, w0, h0) = to.area(); let (item, w1, h1) = match self { Self::W(item, w1) => (item, (*w1).into(), None), @@ -337,6 +339,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Ex impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Min<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("min_draw"); let XYWH(x0, y0, w0, h0) = to.area(); let (item, w1, h1) = match self { Self::W(item, w1) => (item, (*w1).into(), None), @@ -358,6 +361,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Mi impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Max<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("max_draw"); let XYWH(x, y, w0, h0) = to.area(); let (item, w, h) = match self { Self::W(item, w) => (item, (*w).into().unwrap_or(w0), h0), @@ -371,6 +375,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Ma impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Push<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("push_draw"); match self { Self::__(_) => unreachable!(), Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => { @@ -389,6 +394,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pu impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pull<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("pull_draw"); match self { Self::__(_) => unreachable!(), Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => { @@ -413,6 +419,7 @@ impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pu impl <'a, S: Screen, I: Draw, X: Into> + Copy> Draw for Pad<'a, S, I, X> { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("pad_draw"); let XYWH(x, y, w0, h0) = to.area(); let (item, w, h) = match self { Self::X(item, w) => (item, (*w).into().unwrap_or_default(), Default::default()), diff --git a/src/layout/azimuth.rs b/src/layout/azimuth.rs index 9433ed3..53c8853 100644 --- a/src/layout/azimuth.rs +++ b/src/layout/azimuth.rs @@ -116,16 +116,17 @@ pub trait CanAlign<'a, S: Screen>: Draw + Sized { } pub struct Align( - pub(crate) Option, - pub(crate) T, + pub Option, + pub T, ); impl<'a, S: Screen, T: Draw> Draw for Align { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("align_draw"); let Self(azimuth, item) = self; let area0 = to.area(); let size = to.size(area0, item)?; - let area1 = align::(area0, size, *azimuth); + let area1 = aligned::(area0, size, *azimuth); //println!("\n\r{azimuth:?} {area0:?} {size:?}=>{area1:?}"); Ok(if let Some(area) = area1 { to.draw(area, &item)? @@ -135,9 +136,10 @@ impl<'a, S: Screen, T: Draw> Draw for Align { } } -fn align ( +fn aligned ( area0: XYWH, area: Option>, azimuth: Option ) -> Option> { + #[cfg(feature = "prof")] profiling::scope!("align"); area.map(|XYWH(x, y, w, h)|{ let XYWH(x0, y0, w0, h0) = area0; match azimuth { @@ -158,6 +160,7 @@ fn align ( } fn_kw_layout!(kw_split |state, output, expr| { + #[cfg(feature = "prof")] profiling::scope!("kw_split"); let thunk_a = draw(move|screen|ok_flat(expr.nth(1)?.map(|x|state.interpret(screen, x)))); let thunk_b = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); Ok(match expr.head()? { @@ -205,6 +208,7 @@ pub fn split <'a, S: Screen, A: Draw, B: Draw> ( impl<'a, S: Screen, A: Draw, B: Draw> Draw for Pair { fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("pair_draw"); let Self(split, a, b, ..) = self; let (area_a, area_b) = stack_areas(split, to, a, b)?; let (drawn_a, drawn_b) = draw_stacks(split, to, a, area_a, None, b, area_b, None)?; @@ -222,6 +226,7 @@ fn draw_stacks <'a, S: Screen> ( area_b: impl Into>>, origin_b: impl Into>, ) -> UsuallyRef<'a, (Option>, Option>)> { + #[cfg(feature = "prof")] profiling::scope!("draw_stacks"); let draw_a = |to: &mut S|Ok::<_, Box>(match origin_a.into() { Some(origin_a) => to.draw(area_a.into(), a.align(origin_a))?, None => to.draw(area_a.into(), a)? @@ -303,6 +308,7 @@ fn stack_drawn ( drawn_a: Option>, drawn_b: Option>, ) -> Option> { + #[cfg(feature = "prof")] profiling::scope!("stack_drawn"); if let (Some(XYWH(xa, ya, wa, ha)), Some(XYWH(xb, yb, wb, hb))) = (drawn_a, drawn_b) { match split { Split::South => Some(XYWH(xa.min(xb), ya, wa.max(wb), ha + hb)), diff --git a/src/layout/cond.rs b/src/layout/cond.rs index 9dab1d1..496f575 100644 --- a/src/layout/cond.rs +++ b/src/layout/cond.rs @@ -1,11 +1,28 @@ use crate::*; -fn_kw_layout!(kw_when |state, output, expr| { - let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); - ok_flat(matches!(expr.head()?, Some("when")).then(||{ - when(state.namespace(&expr.nth(1)?)?.unwrap(), thunk).draw(output) - })) -}); +pub struct When(pub bool, pub T); + +impl> Draw for When { + fn draw (&self, to: &mut S) -> Drawn { + #[cfg(feature = "prof")] profiling::scope!("when"); + if self.0 { + self.1.draw(to) + } else { + Ok(Default::default()) + } + } +} + +impl When { + + fn_kw_layout!(interpret |state, output, expr| { + #[cfg(feature = "prof")] profiling::scope!("kw_when"); + let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); + ok_flat(matches!(expr.head()?, Some("when")).then(||{ + when(state.namespace(&expr.nth(1)?)?.unwrap(), thunk).draw(output) + })) + }); +} /// Only render when condition is true. /// @@ -15,10 +32,11 @@ fn_kw_layout!(kw_when |state, output, expr| { /// # } /// ``` pub const fn when <'a, T: Screen> (condition: bool, item: impl Draw) -> impl Draw { - draw(move|to: &mut T|if condition { item.draw(to) } else { Ok(Default::default()) }) + When(condition, item) } fn_kw_layout!(kw_either |state, output, expr| { + #[cfg(feature = "prof")] profiling::scope!("kw_layout"); let thunk_a = draw(move|screen|ok_flat(expr.nth(2)?.map(|x|state.interpret(screen, x)))); let thunk_b = draw(move|screen|ok_flat(expr.nth(3)?.map(|x|state.interpret(screen, x)))); ok_flat(matches!(expr.head()?, Some("either")).then(||{ @@ -36,5 +54,8 @@ fn_kw_layout!(kw_either |state, output, expr| { pub const fn either <'a, T: Screen> ( condition: bool, a: impl Draw, b: impl Draw ) -> impl Draw { - draw(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) }) + draw(move|to: &mut T|{ + #[cfg(feature = "prof")] profiling::scope!("either"); + if condition { a.draw(to) } else { b.draw(to) } + }) } diff --git a/src/lib.rs b/src/lib.rs index e4efc8d..d68e831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,9 +68,32 @@ mod deps; pub use self::deps::*; } /// Implement [`Debug`] in bulk. -#[macro_export] macro_rules! impl_debug (($($S:ty|$self:ident,$w:ident|$body:block)*)=>{ - $(impl std::fmt::Debug for $S { fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body })* -}); +#[macro_export] macro_rules! impl_debug ( + (<$($T:ident $(: $U:ident)?),+> $S:ty|$self:ident,$w:ident|$body:block)=>{ + impl <$($T$(:$U)?),+> std::fmt::Debug for $S { + fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body + } + }; + ($S:ty|$self:ident,$w:ident|$body:block)=>{ + impl std::fmt::Debug for $S { + fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body + } + }; +); + +/// Implement [`Display`] in bulk. +#[macro_export] macro_rules! impl_display ( + (<$($T:ident $(: $U:ident)?),+> $S:ty|$self:ident,$w:ident|$body:block)=>{ + impl <$($T$(:$U)?),+> std::fmt::Display for $S { + fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body + } + }; + ($S:ty|$self:ident,$w:ident|$body:block)=>{ + impl std::fmt::Display for $S { + fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body + } + }; +); /// Implement [`From`] in bulk. #[macro_export] macro_rules! impl_from ( @@ -280,7 +303,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } /// Run something with the client. pub fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { - match &*self.0.read().unwrap() { + match &*self.0.try_read().unwrap() { Inert => panic!("jack client not activated"), Inactive(client) => op(client), Activating => panic!("jack client has not finished activation"), @@ -293,14 +316,14 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } let client_state = self.0.clone(); let app: Arc> = Arc::new(RwLock::new(init(self)?)); let mut state = Activating; - std::mem::swap(&mut*client_state.write().unwrap(), &mut state); + std::mem::swap(&mut*client_state.try_write().unwrap(), &mut state); if let Inactive(client) = state { // This is the misc notifications handler. It's a struct that wraps a [Box] // which performs type erasure on a callback that takes [JackEvent], which is // one of the available misc notifications. let notify = JackNotify(Box::new({ let app = app.clone(); - move|event|(&mut*app.write().unwrap()).handle(event) + move|event|(&mut*app.try_write().unwrap()).handle(event) }) as BoxedJackEventHandler); // This is the main processing handler. It's a struct that wraps a [Box] // which performs type erasure on a callback that takes [Client] and [ProcessScope] @@ -308,14 +331,14 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } // implements audio and MIDI input and output on a realtime basis. let process = ::jack::contrib::ClosureProcessHandler::new(Box::new({ let app = app.clone(); - move|c: &_, s: &_|if let Ok(mut app) = app.write() { + move|c: &_, s: &_|if let Some(mut app) = app.try_write() { app.process(c, s) } else { Control::Quit } }) as BoxedAudioHandler); // Launch a client with the two handlers. - *client_state.write().unwrap() = Active( + *client_state.try_write().unwrap() = Active( client.activate_async(notify, process)? ); } else { @@ -445,7 +468,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } fn callback ( state: &Arc>, client: &Client, scope: &ProcessScope ) -> Control where Self: Sized { - if let Ok(mut state) = state.write() { + if let Some(mut state) = state.try_write() { state.process(client, scope) } else { Control::Quit @@ -612,10 +635,10 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } for connect in self.connections().iter() { match &connect.name { Some(Exact(name)) => { - *connect.status.write().unwrap() = self.connect_exact(name)?; + *connect.status.try_write().unwrap() = self.connect_exact(name)?; }, Some(RegExp(re)) => { - *connect.status.write().unwrap() = self.connect_regexp(re, connect.scope)?; + *connect.status.try_write().unwrap() = self.connect_regexp(re, connect.scope)?; }, _ => {}, }; @@ -922,7 +945,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } self.output_buffer[sample].push(self.note_buffer.clone()); // Update the list of currently held notes. if let LiveEvent::Midi { ref message, .. } = event { - update_keys(&mut*self.held.write().unwrap(), message); + update_keys(&mut*self.held.try_write().unwrap(), message); } } /// Write a chunk of MIDI data from the output buffer to the output port. @@ -1079,7 +1102,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } pub fn info (&self) -> Arc { format!(" ({}) {} {}", { - let status = self.status.read().unwrap(); + let status = self.status.try_read().unwrap(); let mut ok = 0; for (_, _, state) in status.iter() { if *state == Connected { @@ -1167,6 +1190,8 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } use crate::time::PerfModel; #[derive(Debug)] pub struct Task { + /// Human-friendly name + pub name: Arc, /// Exit flag. pub exit: Arc, /// Performance counter. @@ -1177,30 +1202,37 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } impl Task { /// Spawn a TUI thread that runs `callt least one, then repeats until `exit`. - pub fn new (exit: Arc, mut call: F) -> Result - where F: FnMut(&PerfModel)->() + Send + Sync + 'static - { + pub fn new () + Send + Sync + 'static> ( + name: Option>, + exit: Arc, + mut call: F + ) -> Result { let perf = Arc::new(PerfModel::default()); + let name: Arc = name.map(|x|x.as_ref().into()).unwrap_or_else(||"tengri".into()); Ok(Self { exit: exit.clone(), perf: perf.clone(), - join: Builder::new().name("tengri task".into()).spawn(move || { + join: Builder::new().name(name.as_ref().into()).spawn(move || { #[cfg(feature = "prof")] profiling::register_thread!(); while !exit.fetch_and(true, Relaxed) { let _ = perf.cycle(&mut call); } - })?.into() + })?.into(), + name }) } /// Spawn a thread that runs `call` least one, then repeats /// until `exit`, sleeping for `time` msec after every iteration. pub fn new_sleep ( - exit: Arc, time: Duration, mut call: F + name: Option>, + exit: Arc, + time: Duration, + mut call: F ) -> Result where F: FnMut(&PerfModel)->() + Send + Sync + 'static { - Self::new(exit, move |perf| { + Self::new(name, exit, move |perf| { let _ = call(perf); sleep(time); }) @@ -1209,11 +1241,14 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// Spawn a thread that uses [crossterm::event::poll] /// to run `call` every `time` msec. #[cfg(feature = "term")] pub fn new_poll ( - exit: Arc, time: Duration, mut call: F + name: Option>, + exit: Arc, + time: Duration, + mut call: F ) -> Result where F: FnMut(&PerfModel)->() + Send + Sync + 'static { - Self::new(exit, move |perf| { + Self::new(name, exit, move |perf| { if poll(time).is_ok() { let _ = call(perf); } @@ -1366,7 +1401,19 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } } - impl<'a, S: Screen, D: Draw> Draw for Option { + impl> Draw for Arc { + fn draw (&self, to: &mut S) -> Drawn { + (**self).draw(to) + } + } + + impl> Draw for Box { + fn draw (&self, to: &mut S) -> Drawn { + (**self).draw(to) + } + } + + impl> Draw for Option { fn draw (&self, to: &mut S) -> Drawn { self.as_ref().map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default) } @@ -1378,12 +1425,6 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } //} //} - //impl> Draw for Arc { - //fn draw (&self, __: &mut T) -> Perhaps> { - //todo!() - //} - //} - impl<'a, T: Screen, V: Draw> Draw for &V { fn draw (&self, to: &mut T) -> Drawn { (*self).draw(to) diff --git a/src/term.rs b/src/term.rs index f4b739b..8b19db3 100644 --- a/src/term.rs +++ b/src/term.rs @@ -162,11 +162,11 @@ impl Tui { ) -> Result { let exited = exited.clone(); let state = state.clone(); - Task::new_poll(exited.clone(), poll, move |_| { + Task::new_poll(Some("tengri input"), exited.clone(), poll, move |_| { let event = read().unwrap(); if Exit::is(&event) { exited.store(true, Relaxed); - } else if let Err(e) = state.write().unwrap().apply(&TuiEvent(event)) { + } else if let Err(e) = state.write().apply(&TuiEvent(event)) { panic!("{e}") } }) @@ -268,15 +268,15 @@ impl Tui { let Size { width, height } = backend.size().expect("get size failed"); let mut prev = Tui::new(width, height); let mut next = Tui::new(width, height); - Ok(Task::new_sleep(exited.clone(), sleep, move |perf| { + Ok(Task::new_sleep(Some("tengri output"), exited.clone(), sleep, move |perf| { let Size { width, height } = backend.size().expect("get size failed"); - if let Ok(state) = state.try_read() { + if let Some(state) = state.try_read() { prev.resize(&mut backend, width, height); state.draw(&mut next).expect("draw failed"); // TODO draw error prev.redraw(&mut backend, &mut next); } - let timer = format!("{:>3.3}ms", perf.used.load(Relaxed)); - prev.blit(&timer, 0, 0, Some(Style::default())); + //let timer = format!("{:>3.3}ms", perf.used.load(Relaxed)); + //prev.blit(&timer, 0, 0, Some(Style::default())); })?) } /// Draw TUI content or its error message.