diff --git a/Cargo.lock b/Cargo.lock index 5648fc06f..af4659610 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -671,6 +671,7 @@ dependencies = [ "device_query", "either", "enigo", + "futures", "futures-timer", "helgobox-api", "indexmap 2.1.0", @@ -685,6 +686,7 @@ dependencies = [ "serde", "serde_json", "thiserror", + "tokio", "tracing", "xxhash-rust", ] @@ -5167,6 +5169,7 @@ dependencies = [ "erased-serde", "fragile", "function_name", + "futures", "glidesort", "helgoboss-learn", "helgoboss-license-api", diff --git a/base/Cargo.toml b/base/Cargo.toml index 673516263..ed5296d45 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -33,6 +33,8 @@ anyhow.workspace = true thiserror.workspace = true camino.workspace = true indexmap.workspace = true +futures.workspace = true +tokio.workspace = true [target.'cfg(target_os = "macos")'.dependencies] # For not letting device_query panic when macOS accessibility permissions not granted diff --git a/base/src/lib.rs b/base/src/lib.rs index a5f7f08cc..bee35c5b8 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -47,3 +47,5 @@ pub mod panic_util; mod approx_f64; pub use approx_f64::*; + +pub mod replenishment_channel; diff --git a/base/src/replenishment_channel.rs b/base/src/replenishment_channel.rs new file mode 100644 index 000000000..cef3b8d51 --- /dev/null +++ b/base/src/replenishment_channel.rs @@ -0,0 +1,49 @@ +use futures::future::BoxFuture; +use tokio::sync::mpsc::Receiver; +use tracing::debug; + +/// An orchestration (task and receiver) to be used to supply the receiver with spare parts that might or might not be +/// necessary. +/// +/// This is usually used in scenarios where the consumer lives in a thread that is not allowed to allocate +/// (for example, real-time threads). +pub struct ReplenishmentOrchestration { + pub task: F, + pub receiver: ReplenishmentReceiver, +} + +/// Creates an orchestration. +/// +/// The capacity should be very low, depending on how many spare items you want to create. +pub fn orchestrate_replenishment( + capacity: usize, + mut create_next_item: impl FnMut() -> T + Send + 'static, +) -> ReplenishmentOrchestration> +where + T: Send + 'static, +{ + let (sender, receiver) = tokio::sync::mpsc::channel::(capacity); + let task = async move { + while let Ok(permit) = sender.reserve().await { + debug!("Replenishment channel has capacity. Create next item."); + let item = create_next_item(); + permit.send(item); + } + }; + ReplenishmentOrchestration { + receiver: ReplenishmentReceiver { receiver }, + task: Box::pin(task), + } +} + +#[derive(Debug)] +pub struct ReplenishmentReceiver { + receiver: Receiver, +} + +impl ReplenishmentReceiver { + /// Returns the next available item if one is available. + pub fn request_item(&mut self) -> Option { + self.receiver.try_recv().ok() + } +} diff --git a/main/src/infrastructure/plugin/backbone_shell.rs b/main/src/infrastructure/plugin/backbone_shell.rs index f4f08bb35..65f2c22f4 100644 --- a/main/src/infrastructure/plugin/backbone_shell.rs +++ b/main/src/infrastructure/plugin/backbone_shell.rs @@ -2981,6 +2981,7 @@ mod playtime_impl { use base::metrics_util::{record_duration, record_occurrence}; use base::spawn_in_main_thread; use camino::Utf8PathBuf; + use futures::future::BoxFuture; use playtime_api::persistence::PlaytimeSettings; use playtime_clip_engine::PlaytimeEngine; use reaper_high::{GroupingBehavior, Project, Reaper}; @@ -3061,8 +3062,8 @@ mod playtime_impl { None }; #[derive(Debug)] - struct RealearnPlaytimeIntegration; - impl playtime_clip_engine::PlaytimeIntegration for RealearnPlaytimeIntegration { + struct HelgoboxPlaytimeIntegration; + impl playtime_clip_engine::PlaytimeIntegration for HelgoboxPlaytimeIntegration { fn export_to_clipboard( &self, item: &dyn playtime_clip_engine::PlaytimeItem, @@ -3086,12 +3087,16 @@ mod playtime_impl { fs::write(settings_path, json)?; Ok(()) } + + fn spawn_in_async_runtime(&self, f: BoxFuture<'static, ()>) { + BackboneShell::get().spawn_in_async_runtime(f); + } } let args = playtime_clip_engine::PlaytimeEngineInitArgs { available_licenses: license_manager.licenses(), settings: BackboneShell::read_playtime_settings(), metrics_recorder, - integration: Box::new(RealearnPlaytimeIntegration), + integration: Box::new(HelgoboxPlaytimeIntegration), }; PlaytimeEngine::make_available_globally(PlaytimeEngine::new(args)); } diff --git a/playtime-clip-engine b/playtime-clip-engine index e2c862110..676eb6fbc 160000 --- a/playtime-clip-engine +++ b/playtime-clip-engine @@ -1 +1 @@ -Subproject commit e2c862110bb12296287232c13e83a8211b25895a +Subproject commit 676eb6fbc157a2e9d5222d24cebd1a4f77a06b91