I have a trait called Stoppable
that makes it possible to create struct instances that have their own thread and that stop this thread when they are dropped.
I have multiple structs that need to implement Stoppable
. For example: RtspClient
, Decoder
, Renderer
, ImageProcessor
.
The problem is that to implement Stoppable
for each of them, I need to add
should_continue: Arc<AtomicBool>,
run_thread: Option<JoinHandle<()>>,
to all of them.
Also, I tried doing this:
fn should_continue(&self) -> bool {
self.should_continue.load(Ordering::Relaxed)
}
but I ended up having to ditch it and use this function inside the while like this:
while should_continue_.load(Ordering::Relaxed) {
because of the closure (otherwise it'd move the borrowed &self
).
Take a look:
use super::decoded_packet::DecodedPacket;
use super::decoder::{Codec, Decoder};
use super::defaults;
use super::encoded_packet::EncodedPacket;
use super::stoppable::Stoppable;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{sleep, spawn, JoinHandle};
pub struct DummyDecoder {
pub on_consume: Arc<dyn Fn() -> Option<EncodedPacket> + Send + Sync + 'static>,
pub on_produce: Arc<dyn Fn(DecodedPacket) + Send + Sync + 'static>,
should_continue: Arc<AtomicBool>,
run_thread: Option<JoinHandle<()>>,
}
impl DummyDecoder {
pub fn new(
on_consume: Arc<dyn Fn() -> Option<EncodedPacket> + Send + Sync + 'static>,
on_produce: Arc<dyn Fn(DecodedPacket) + Send + Sync + 'static>,
) -> DummyDecoder {
DummyDecoder {
on_consume: on_consume,
on_produce: on_produce,
should_continue: Arc::new(AtomicBool::new(true)),
run_thread: None,
}
}
}
impl Decoder for DummyDecoder {
fn set_on_consume(
&mut self,
f: Arc<dyn Fn() -> Option<EncodedPacket> + Send + Sync + 'static>,
) {
self.on_consume = f;
}
fn set_on_produce(&mut self, f: Arc<dyn Fn(DecodedPacket) + Send + Sync + 'static>) {
self.on_produce = f;
}
fn codec(&self) -> Codec {
Codec::H264
}
}
impl Stoppable for DummyDecoder {
fn should_continue(&self) -> bool {
self.should_continue.load(Ordering::Relaxed)
}
fn run(&mut self) {
let on_consume_ = self.on_consume.clone();
let on_produce_ = self.on_produce.clone();
let should_continue_ = self.should_continue.clone();
self.run_thread = Some(spawn(move || {
while should_continue_.load(Ordering::Relaxed) {
let encoded_packet = (on_consume_)();
match encoded_packet{
Some(encoded_packet) => {
println!("decoder received packet!")
}
None => {}
}
//Simulate transform of encoded_packet in decoded_packet here
sleep(defaults::default_timeout_stoppable);
let decoded_packet = DecodedPacket { data: Vec::new() };
(on_produce_)(decoded_packet);
}
}));
}
fn stop(&self) {
self.should_continue.store(false, Ordering::Relaxed);
}
}
impl Drop for DummyDecoder {
fn drop(&mut self) {
self.stop();
self.run_thread.take().unwrap().join();
}
}
On C++, should_continue
, run_thread
and fn should_continue()
would be shared in a common base class. I just want to confirm that there's no way to reuse these variables and functions, because I hate copying the same code over and over for the same class.