This commit is contained in:
Anatoly Yakovenko 2018-03-24 18:01:54 -07:00
parent f52f02a434
commit 7732f3f5fb
1 changed files with 221 additions and 0 deletions

221
src/services.rs Normal file
View File

@ -0,0 +1,221 @@
//! Small services library with named ports
//! see test for usage
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{spawn, JoinHandle};
use std::time::Duration;
use streamer;
use result::Result;
use result::Error;
pub enum Port {
Main,
PacketReader,
Accountant,
PacketSender,
}
impl Port {
fn to_usize(self) -> usize {
match self {
Port::Main => 0,
Port::PacketReader => 1,
Port::Accountant => 2,
Port::PacketSender => 3,
}
}
}
#[derive(Clone)]
pub enum Data {
Signal,
SharedPacketData(streamer::SharedPacketData),
}
struct Locked {
ports: Vec<Sender<Data>>,
readers: Vec<Arc<Mutex<Receiver<Data>>>>,
threads: Vec<Arc<Option<JoinHandle<Result<()>>>>>,
}
pub struct Services {
lock: Arc<RwLock<Locked>>,
exit: Arc<AtomicBool>,
}
pub type Ports = Vec<Sender<Data>>;
impl Services {
pub fn new() -> Services {
let (s1, r1) = channel();
let (s2, r2) = channel();
let (s3, r3) = channel();
let (s4, r4) = channel();
let (s5, r5) = channel();
let locked = Locked {
ports: [s1, s2, s3, s4, s5].to_vec(),
readers: [
Arc::new(Mutex::new(r1)),
Arc::new(Mutex::new(r2)),
Arc::new(Mutex::new(r3)),
Arc::new(Mutex::new(r4)),
Arc::new(Mutex::new(r5)),
].to_vec(),
threads: [
Arc::new(None),
Arc::new(None),
Arc::new(None),
Arc::new(None),
Arc::new(None),
].to_vec(),
};
let exit = Arc::new(AtomicBool::new(false));
Services {
lock: Arc::new(RwLock::new(locked)),
exit: exit,
}
}
pub fn source<F>(&self, port: Port, func: F) -> Result<()>
where
F: Send + 'static + Fn(&Ports) -> Result<()>,
{
let mut w = self.lock.write().unwrap();
let pz = port.to_usize();
if w.threads[pz].is_some() {
return Err(Error::Services);
}
let c_ports = w.ports.clone();
let c_exit = self.exit.clone();
let j = spawn(move || loop {
match func(&c_ports) {
Ok(()) => (),
e => return e,
}
if c_exit.load(Ordering::Relaxed) == true {
return Ok(());
}
});
w.threads[pz] = Arc::new(Some(j));
return Ok(());
}
pub fn listen<F>(&mut self, port: Port, func: F) -> Result<()>
where
F: Send + 'static + Fn(&Ports, Data) -> Result<()>,
{
let mut w = self.lock.write().unwrap();
let pz = port.to_usize();
if w.threads[pz].is_some() {
return Err(Error::Services);
}
let recv_lock = w.readers[pz].clone();
let c_ports = w.ports.clone();
let c_exit = self.exit.clone();
let j: JoinHandle<Result<()>> = spawn(move || loop {
let recv = recv_lock.lock().unwrap();
let timer = Duration::new(0, 500000);
match recv.recv_timeout(timer) {
Ok(val) => func(&c_ports, val).expect("services listen"),
_ => (),
}
if c_exit.load(Ordering::Relaxed) {
return Ok(());
}
});
w.threads[pz] = Arc::new(Some(j));
return Ok(());
}
pub fn send(ports: &Ports, to: Port, m: Data) -> Result<()> {
ports[to.to_usize()]
.send(m)
.or_else(|_| Err(Error::SendError))
}
pub fn join(&mut self) -> Result<()> {
let pz = Port::Main.to_usize();
let recv = self.lock.write().unwrap().readers[pz].clone();
recv.lock().unwrap().recv()?;
self.shutdown()?;
return Ok(());
}
pub fn shutdown(&mut self) -> Result<()> {
self.exit.store(true, Ordering::Relaxed);
let r = self.lock.read().unwrap();
for t in r.threads.iter() {
match Arc::try_unwrap((*t).clone()) {
Ok(Some(j)) => j.join()??,
_ => (),
};
}
return Ok(());
}
}
#[cfg(test)]
mod test {
use services::Services;
use services::Port::{Accountant, Main, PacketReader};
use services::Data::Signal;
use std::sync::{Arc, Mutex};
#[test]
fn test_init() {
let mut o = Services::new();
assert_matches!(o.shutdown(), Ok(()));
}
#[test]
fn test_join() {
let mut o = Services::new();
assert_matches!(
o.source(PacketReader, move |ports| Services::send(
ports,
Main,
Signal
)),
Ok(())
);
assert_matches!(o.join(), Ok(()));
}
#[test]
fn test_source() {
let mut o = Services::new();
assert_matches!(
o.source(PacketReader, move |ports| Services::send(
ports,
Main,
Signal
)),
Ok(())
);
assert!(o.source(PacketReader, move |_ports| Ok(())).is_err());
assert!(o.listen(PacketReader, move |_ports, _data| Ok(())).is_err());
assert_matches!(o.join(), Ok(()));
}
#[test]
fn test_listen() {
let mut o = Services::new();
let val = Arc::new(Mutex::new(false));
assert_matches!(
o.source(PacketReader, move |ports| Services::send(
ports,
Accountant,
Signal
)),
Ok(())
);
let c_val = val.clone();
assert_matches!(
o.listen(Accountant, move |ports, data| match data {
Signal => {
*c_val.lock().unwrap() = true;
Services::send(ports, Main, Signal)
}
_ => Ok(()),
}),
Ok(())
);
assert_matches!(o.join(), Ok(()));
assert_eq!(*val.lock().unwrap(), true);
}
}