diff options
author | tcmal <me@aria.rip> | 2024-09-08 18:46:43 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-09-12 16:30:09 +0100 |
commit | 9a7042df7f9a7ad0324c4442ae918c2a4a442966 (patch) | |
tree | 81324f03df3ddaac9db7d1e01ef5b319bef659ef /crates/control/src | |
parent | ddc97e9361137fde6f0894ffa729d285159662fa (diff) |
Add framework for separate printer control thread
Diffstat (limited to 'crates/control/src')
-rw-r--r-- | crates/control/src/lib.rs | 81 |
1 files changed, 81 insertions, 0 deletions
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, + } + } + } +} |