From 1c134a78de669ee4e95b53ca2ac3dc6aaea9a677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 10 Mar 2025 12:30:11 +0100 Subject: [PATCH] refactor(multiverse): Use a mpsc channel to propagate status messages to the status widget --- labs/multiverse/src/status.rs | 71 +++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/labs/multiverse/src/status.rs b/labs/multiverse/src/status.rs index 014195e96..8f08673a3 100644 --- a/labs/multiverse/src/status.rs +++ b/labs/multiverse/src/status.rs @@ -1,9 +1,16 @@ use std::{ - sync::{Arc, Mutex}, + sync::{ + mpsc::{self, Receiver}, + Arc, Mutex, + }, time::Duration, }; -use tokio::{spawn, task::JoinHandle, time::sleep}; +use tokio::{ + spawn, + task::{spawn_blocking, JoinHandle}, + time::sleep, +}; const MESSAGE_DURATION: Duration = Duration::from_secs(4); @@ -11,32 +18,56 @@ pub struct Status { /// Content of the latest status message, if set. pub last_status_message: Arc>>, - /// A task to automatically clear the status message in N seconds, if set. - pub clear_status_message: Option>, + message_sender: mpsc::Sender, + + _receiver_task: JoinHandle<()>, } impl Status { /// Create a new empty [`Status`] widget. pub fn new() -> Self { - Self { last_status_message: Default::default(), clear_status_message: Default::default() } + let (message_sender, receiver) = mpsc::channel(); + let last_status_message = Arc::new(Mutex::new(None)); + + let receiver_task = spawn_blocking({ + let last_status_message = last_status_message.clone(); + move || Self::receiving_task(receiver, last_status_message) + }); + + Self { last_status_message, _receiver_task: receiver_task, message_sender } + } + + fn receiving_task(receiver: Receiver, status_message: Arc>>) { + let mut clear_message_task: Option> = None; + + while let Ok(message) = receiver.recv() { + if let Some(task) = clear_message_task.take() { + task.abort(); + } + + { + let mut status_message = status_message.lock().unwrap(); + *status_message = Some(message); + } + + clear_message_task = Some(spawn({ + let status_message = status_message.clone(); + + async move { + // Clear the status message in 4 seconds. + sleep(MESSAGE_DURATION).await; + status_message.lock().unwrap().take(); + } + })); + } } /// Set the current status message (displayed at the bottom), for a few /// seconds. - pub fn set_message(&mut self, status: String) { - if let Some(handle) = self.clear_status_message.take() { - // Cancel the previous task to clear the status message. - handle.abort(); - } - - *self.last_status_message.lock().unwrap() = Some(status); - - let message = self.last_status_message.clone(); - self.clear_status_message = Some(spawn(async move { - // Clear the status message in 4 seconds. - sleep(MESSAGE_DURATION).await; - - *message.lock().unwrap() = None; - })); + pub fn set_message(&self, status: String) { + self.message_sender.send(status).expect( + "We should be able to send the status message since the receiver is alive \ + as long as we are alive", + ); } }