use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
use std::thread::JoinHandle;
use serde_json::Value;
use crate::boundary::reply::Reply;
use crate::git_tree::Stream;
use crate::watch::Repaint;
use super::asker::ASK_PERIOD;
use super::client::Seat;
pub struct Tail {
subject: Sender<Option<Value>>,
frames: Receiver<(String, Option<Stream>)>,
standing: Option<Value>,
asked: Option<Value>,
landed: Option<Stream>,
}
pub struct TailEnd {
subject: Receiver<Option<Value>>,
frames: Sender<(String, Option<Stream>)>,
standing: Option<Value>,
hung_up: bool,
}
pub fn pair() -> (Tail, TailEnd) {
let (s_tx, s_rx) = channel();
let (f_tx, f_rx) = channel();
(
Tail {
subject: s_tx,
frames: f_rx,
standing: None,
asked: None,
landed: None,
},
TailEnd {
subject: s_rx,
frames: f_tx,
standing: None,
hung_up: false,
},
)
}
impl Default for Tail {
fn default() -> Self {
pair().0
}
}
impl Tail {
pub fn ask(&mut self, question: &Value) -> Option<Stream> {
self.standing = Some(question.clone());
self.landed.clone()
}
pub fn settle(&mut self) {
let key = self.asked.as_ref().map(Value::to_string);
for (answered, frame) in self.frames.try_iter() {
if Some(&answered) == key.as_ref() {
self.landed = frame;
}
}
if self.standing != self.asked {
let _ = self.subject.send(self.standing.clone());
self.asked = self.standing.clone();
self.landed = None;
}
self.standing = None;
}
}
impl TailEnd {
pub fn standing(&mut self) -> Option<Value> {
loop {
match self.subject.try_recv() {
Ok(newest) => self.standing = newest,
Err(TryRecvError::Empty) => return self.standing.clone(),
Err(TryRecvError::Disconnected) => {
self.hung_up = true;
return None;
}
}
}
}
pub fn publish(&self, question: &str, frame: Option<Stream>) {
let _ = self.frames.send((question.to_owned(), frame));
}
}
pub struct Lane {
seat: Seat,
end: TailEnd,
repaint: Arc<dyn Repaint>,
}
impl Lane {
pub fn new(seat: Seat, end: TailEnd, repaint: Arc<dyn Repaint>) -> Self {
Self { seat, end, repaint }
}
pub fn turn(&mut self) -> bool {
let Self { seat, end, repaint } = self;
let Some(question) = end.standing() else {
return false;
};
let key = question.to_string();
let mut landed = false;
let _ = seat.followed(&question, &mut |frame| {
let Ok(Reply::Follow(stream)) = frame else {
return false;
};
end.publish(&key, Some(stream));
repaint.request();
landed = true;
end.standing().as_ref() == Some(&question)
});
end.publish(&key, None);
repaint.request();
landed
}
pub fn start(mut self) -> LaneThread {
let stop = Arc::new(AtomicBool::new(false));
let flag = Arc::clone(&stop);
let handle = std::thread::spawn(move || {
while !flag.load(Ordering::Relaxed) {
if !self.turn() {
std::thread::park_timeout(ASK_PERIOD);
}
}
});
LaneThread {
stop,
handle: Some(handle),
}
}
}
pub struct LaneThread {
stop: Arc<AtomicBool>,
handle: Option<JoinHandle<()>>,
}
impl Drop for LaneThread {
fn drop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(handle) = self.handle.take() {
handle.thread().unpark();
}
}
}
#[cfg(test)]
mod tests;