From d2fd2da41dbc9c2f19711c14cd68142d5484eb40 Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Sun, 2 May 2021 16:01:55 +0200 Subject: Factor out all unsafe code into its own module (unfinished) --- pulse/src/communication.rs | 28 +++++++++++----- pulse/src/config.rs | 12 +++---- pulse/src/runnable.rs | 36 -------------------- pulse/src/runnable/mod.rs | 34 +++++++++++++++++++ pulse/src/runnable/pulse.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+), 50 deletions(-) delete mode 100644 pulse/src/runnable.rs create mode 100644 pulse/src/runnable/mod.rs create mode 100644 pulse/src/runnable/pulse.rs (limited to 'pulse/src') diff --git a/pulse/src/communication.rs b/pulse/src/communication.rs index c8578b7..cfb800e 100644 --- a/pulse/src/communication.rs +++ b/pulse/src/communication.rs @@ -1,5 +1,5 @@ use std::sync::mpsc::*; -use crate::runnable::*; +use crate::runnable::pulse::*; use swaystatus_plugin::*; pub enum MessagesFromMain { @@ -9,21 +9,27 @@ pub enum MessagesFromMain { pub struct SenderForMain { sender : Sender, - pulse_loop : std::sync::Arc + pulse_waker : PulseWakeUp } impl<'p> SenderForMain { - pub fn new(sender : Sender, pulse_loop : std::sync::Arc) -> Self { + pub fn new(sender : Sender, pulse_waker : PulseWakeUp) -> Self { SenderForMain{ sender, - pulse_loop + pulse_waker } } - fn send(&self, message : MessagesFromMain) -> Result<(), PluginCommunicationError> - { - //TODO!!!! - Ok(()) + fn send(&self, message : MessagesFromMain) -> Result<(), PluginCommunicationError> { + if let Ok(_) = self.sender.send(message) { + //The cool thing about pulse using poll() is that poll() also wakes up if started after + //the actual wake up call. So no need to worry about races, this is inherently sane! + self.pulse_waker.wake_up()?; + Ok(()) + } + else { + Err(PluginCommunicationError{}) + } } } @@ -35,3 +41,9 @@ impl MsgMainToModule for SenderForMain { self.send(MessagesFromMain::Refresh) } } + +impl From for PluginCommunicationError { + fn from(error : PulseWakeUpError) -> Self { + PluginCommunicationError {} + } +} diff --git a/pulse/src/config.rs b/pulse/src/config.rs index 77b2a22..efe5803 100644 --- a/pulse/src/config.rs +++ b/pulse/src/config.rs @@ -5,7 +5,7 @@ use swaystatus_plugin::*; #[derive(Serialize, Deserialize)] #[serde(tag = "Sink")] -enum Sink { +pub enum Sink { Default, Specific { sink_name : String @@ -14,7 +14,7 @@ enum Sink { #[derive(Serialize, Deserialize)] #[serde(tag = "Format")] -enum Volume { +pub enum Volume { Off, Numeric { label : String @@ -27,7 +27,7 @@ enum Volume { #[derive(Serialize, Deserialize)] #[serde(tag = "Format")] -enum Balance { +pub enum Balance { Off, Numeric { label : String @@ -42,9 +42,9 @@ enum Balance { #[derive(Serialize, Deserialize)] #[serde(rename_all = "PascalCase", default)] pub struct PulseVolumeConfig { - sink : Sink, - volume : Volume, - balance : Balance + pub sink : Sink, + pub volume : Volume, + pub balance : Balance } impl Default for PulseVolumeConfig { diff --git a/pulse/src/runnable.rs b/pulse/src/runnable.rs deleted file mode 100644 index d77a132..0000000 --- a/pulse/src/runnable.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::sync::mpsc::*; -use crate::config::*; -use swaystatus_plugin::*; -use crate::communication::*; - -pub struct PulseVolumeRunnable<'p> { - config : &'p PulseVolumeConfig, - to_main : Box, - from_main : Receiver, - pulse : std::sync::Arc -} - -impl<'p : 's, 's> PulseVolumeRunnable<'p> { - pub fn new(config : &'p PulseVolumeConfig, to_main : Box) -> (Self, SenderForMain) { - let (s, r) = channel(); - let pulse = std::sync::Arc::new(PulseMainLoop {});//TODO: initialize this properly - let result = PulseVolumeRunnable { - config, - to_main, - from_main : r, - pulse: pulse.clone() - }; - let sender = SenderForMain::new(s, pulse); - (result, sender) - } -} - -impl<'p> SwayStatusModuleRunnable for PulseVolumeRunnable<'p> { - fn run(&self) { - //TODO - } -} - -pub struct PulseMainLoop { - //TODO! -} diff --git a/pulse/src/runnable/mod.rs b/pulse/src/runnable/mod.rs new file mode 100644 index 0000000..612bca9 --- /dev/null +++ b/pulse/src/runnable/mod.rs @@ -0,0 +1,34 @@ +use std::sync::mpsc::*; +use crate::config::*; +use swaystatus_plugin::*; +use crate::communication::*; + +pub mod pulse; +use pulse::Pulse; + +pub struct PulseVolumeRunnable<'p> { + config : &'p PulseVolumeConfig, + to_main : Box, + from_main : Receiver, + pulse : Pulse +} + +impl<'p : 's, 's> PulseVolumeRunnable<'p> { + pub fn new(config : &'p PulseVolumeConfig, to_main : Box) -> (Self, SenderForMain) { + let (s, r) = channel(); + let result = PulseVolumeRunnable { + config, + to_main, + from_main : r, + pulse: Pulse::init(&config.sink), + }; + let sender = SenderForMain::new(s, result.pulse.get_wake_up()); + (result, sender) + } +} + +impl<'p> SwayStatusModuleRunnable for PulseVolumeRunnable<'p> { + fn run(&self) { + //TODO + } +} diff --git a/pulse/src/runnable/pulse.rs b/pulse/src/runnable/pulse.rs new file mode 100644 index 0000000..388617f --- /dev/null +++ b/pulse/src/runnable/pulse.rs @@ -0,0 +1,80 @@ +//! This module encapsulates all interaction with pulseaudio. +//! It is the only place in this plugin that explicitly includes unsafe code. + +use std::sync::{Arc, Weak}; +use crate::config::Sink; +use std::marker::PhantomData; + +pub(super) struct Pulse { + main_loop : Arc, //Beware: Never ever clone this except for PulseWakeUp! + + //Pulse is _definitely_ not sync. It is supposed to be Send though. + marker : PhantomData> +} + +//In general it's a really stupid idea to Send Arcs of non-Sync types around. +//However this Arc is only cloned under one very specific condition: creating a PulseWakeUp. +//Apart from that one (thread-safe) exception it's actually single-ownership. +unsafe impl Send for Pulse {} + +impl Pulse { + pub fn init(config : &Sink) -> Self { + //TODO:Use the sink parameter. + Pulse { + //TODO: Initialize main_loop on the pulse-side. + main_loop : Arc::new(PulseMainLoop {}), + marker :PhantomData + } + } + pub fn get_wake_up(&self) -> PulseWakeUp { + PulseWakeUp { main_loop : Arc::downgrade(&self.main_loop) } + } +} + +/// Helper to wake up the Pulseaudio main loop. +/// This implements Send and Sync, because it's explicitly meant to allow cross-thread +/// communication. It only exposes the wake up function of pulse, and that function is in itself +/// send and sync (https://github.com/pulseaudio/pulseaudio/blob/master/src/pulse/mainloop.c). +pub struct PulseWakeUp { + main_loop : Weak +} +unsafe impl Send for PulseWakeUp {} +unsafe impl Sync for PulseWakeUp {} + +impl PulseWakeUp { + pub fn wake_up(&self) -> Result<(), PulseWakeUpError> { + match self.main_loop.upgrade() { + Some(main_loop) => { + main_loop.awaken(); + Ok(()) + } + None => { + Err(PulseWakeUpError {} ) + } + } + } +} + +#[derive(Debug)] +pub struct PulseWakeUpError; +impl std::error::Error for PulseWakeUpError {} +impl std::fmt::Display for PulseWakeUpError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f,"Failed to wake up Pulse main loop. It has been dropped already.") + } +} + +struct PulseMainLoop { + //TODO +} +impl PulseMainLoop { + //this is intentionally all private. Nobody outside this module should call anything on this. + fn awaken(&self) { + //TODO! + } +} +impl Drop for PulseMainLoop { + fn drop(&mut self) { + //TODO: if this gets dropped, free the pulse main loop. + } +} -- cgit v1.2.3