diff options
Diffstat (limited to 'crates/control')
-rw-r--r-- | crates/control/Cargo.toml | 9 | ||||
-rw-r--r-- | crates/control/src/lib.rs | 81 |
2 files changed, 90 insertions, 0 deletions
diff --git a/crates/control/Cargo.toml b/crates/control/Cargo.toml new file mode 100644 index 0000000..6934db5 --- /dev/null +++ b/crates/control/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "argonaut-control" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } diff --git a/crates/control/src/lib.rs b/crates/control/src/lib.rs new file mode 100644 index 0000000..5691cf2 --- /dev/null +++ b/crates/control/src/lib.rs @@ -0,0 +1,81 @@ +//! Printer control related code +//! +//! To ensure the volume of web requests doesn't mess with printing, communication with the printer is done +//! in a separate thread, on its own async executor. +//! +//! The main server spawns this thread by creating a [`Handle`] and communicates by sending [messages](crate::Message) over an MPSC channel. + +use std::{ + mem::ManuallyDrop, + thread::{self, JoinHandle}, +}; +use tokio::{ + runtime, + sync::mpsc::{self, Receiver, Sender}, +}; +use tracing::info; + +/// The maximum number of messages to buffer before 'blocking' new ones +/// This provides a form of backpressure +const CHANNEL_CAPACITY: usize = 100; + +/// A control message sent from the API to the printer control thread +pub enum Message { + Quit, +} + +/// A handle to the printer control thread, allowing for communication. +pub struct Handle { + cmd_send: Sender<Message>, + thread_handle: ManuallyDrop<JoinHandle<()>>, +} + +impl Handle { + /// Spawn a printer control thread, returning a handle + pub fn spawn() -> Self { + let (send, recv) = mpsc::channel(CHANNEL_CAPACITY); + + let state = State { cmd_recv: recv }; + let thread_handle = thread::spawn(move || { + let rt = runtime::Builder::new_current_thread() + .build() + .expect("error spawning printer control thread"); + + rt.block_on(async move { state.entrypoint().await }); + }); + + Self { + cmd_send: send, + thread_handle: ManuallyDrop::new(thread_handle), + } + } +} + +impl Drop for Handle { + fn drop(&mut self) { + let _ = self.cmd_send.blocking_send(Message::Quit); + unsafe { + info!("waiting for printer thread to exit..."); + ManuallyDrop::take(&mut self.thread_handle) + .join() + .expect("error joining printer thread") + }; + } +} + +/// The state held by the printer control thread +pub struct State { + cmd_recv: Receiver<Message>, +} + +impl State { + /// The entrypoint of the printer control thread + pub async fn entrypoint(mut self) { + info!("listening for events"); + while let Some(msg) = self.cmd_recv.recv().await { + match msg { + Message::Quit => break, + } + } + } +} |