use std::net::Shutdown;
use std::os::unix::net::UnixStream;
use std::sync::atomic::AtomicBool;
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
use std::thread::{self, JoinHandle};
use crate::core::{Wake, run_loop};
use crate::frame::{read_frame, write_frame};
use crate::protocol::{Command, Event, decode_event, encode_command};
use crate::supervisor::Supervisor;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ExitIntent {
Disconnect,
Quit,
}
pub trait Transport {
fn send(&mut self, cmd: Command);
fn poll(&mut self) -> Vec<Event>;
fn connected(&self) -> bool;
fn shutdown(&mut self, intent: ExitIntent);
}
fn drain(rx: &Receiver<Event>, dead: &mut bool) -> Vec<Event> {
let mut evs = Vec::new();
loop {
match rx.try_recv() {
Ok(ev) => evs.push(ev),
Err(TryRecvError::Empty) => break,
Err(TryRecvError::Disconnected) => {
*dead = true;
break;
}
}
}
evs
}
pub struct ThreadTransport {
wake_tx: Sender<Wake>,
evt_rx: Receiver<Event>,
handle: Option<JoinHandle<()>>,
dead: bool,
}
impl ThreadTransport {
pub fn spawn(mut sup: Supervisor, wait_tx: Sender<()>) -> ThreadTransport {
let (wake_tx, wake_rx) = channel::<Wake>();
let (evt_tx, evt_rx) = channel::<Event>();
sup.set_launch_context(crate::protocol::LaunchContext::here());
sup.set_waker(wake_tx.clone());
let handle = thread::spawn(move || {
let mut sup = sup;
let stop = AtomicBool::new(false);
run_loop(&mut sup, &wake_rx, &stop, |ev| {
if evt_tx.send(ev.clone()).is_err() {
return false; }
let _ = wait_tx.send(());
true
});
});
ThreadTransport {
wake_tx,
evt_rx,
handle: Some(handle),
dead: false,
}
}
fn stop(&mut self) {
let _ = self.wake_tx.send(Wake::Cmd(Command::Shutdown));
if let Some(h) = self.handle.take() {
let _ = h.join();
}
}
}
impl Transport for ThreadTransport {
fn send(&mut self, cmd: Command) {
let _ = self.wake_tx.send(Wake::Cmd(cmd));
}
fn poll(&mut self) -> Vec<Event> {
drain(&self.evt_rx, &mut self.dead)
}
fn connected(&self) -> bool {
!self.dead
}
fn shutdown(&mut self, _intent: ExitIntent) {
self.stop();
}
}
impl Drop for ThreadTransport {
fn drop(&mut self) {
self.stop();
}
}
pub struct SocketTransport {
write: UnixStream,
evt_rx: Receiver<Event>,
reader: Option<JoinHandle<()>>,
dead: bool,
}
impl SocketTransport {
pub fn from_halves(
write: UnixStream,
read: UnixStream,
wait_tx: Sender<()>,
) -> SocketTransport {
let (evt_tx, evt_rx) = channel();
let reader = thread::spawn(move || {
let mut read = read;
while let Ok((kind, payload)) = read_frame(&mut read) {
if let Some(ev) = decode_event(kind, &payload) {
if evt_tx.send(ev).is_err() {
break;
}
let _ = wait_tx.send(());
}
}
let _ = wait_tx.send(());
});
SocketTransport {
write,
evt_rx,
reader: Some(reader),
dead: false,
}
}
}
impl Transport for SocketTransport {
fn send(&mut self, cmd: Command) {
let (kind, payload) = encode_command(&cmd);
let _ = write_frame(&mut self.write, kind, &payload);
}
fn poll(&mut self) -> Vec<Event> {
drain(&self.evt_rx, &mut self.dead)
}
fn connected(&self) -> bool {
!self.dead
}
fn shutdown(&mut self, intent: ExitIntent) {
match intent {
ExitIntent::Quit => self.send(Command::Shutdown),
ExitIntent::Disconnect => {
let _ = self.write.shutdown(Shutdown::Both);
}
}
if let Some(h) = self.reader.take() {
let _ = h.join();
}
}
}
#[cfg(test)]
pub struct LocalTransport {
sup: Supervisor,
}
#[cfg(test)]
impl LocalTransport {
pub fn new(sup: Supervisor) -> LocalTransport {
LocalTransport { sup }
}
}
#[cfg(test)]
impl Transport for LocalTransport {
fn send(&mut self, cmd: Command) {
self.sup.apply(cmd);
}
fn poll(&mut self) -> Vec<Event> {
self.sup.tick();
self.sup.drain()
}
fn connected(&self) -> bool {
true
}
fn shutdown(&mut self, _intent: ExitIntent) {
self.sup.apply(Command::Shutdown);
}
}