use std::collections::VecDeque;
use std::mem;
use std::sync::mpsc;
use std::thread::{self, ThreadId};
use std::time::{Duration, Instant};
use tokio::sync::oneshot;
use crate::event::CallRecord;
use crate::event::assemble::EventJob;
const QUEUE_FULL: &str = "Event queue full, dropping oldest event";
const SHUTTING_DOWN: &str = "Queue is shutting down, event dropped";
const FOREVER: Duration = Duration::from_secs(100 * 365 * 24 * 60 * 60);
#[derive(Debug)]
pub(super) enum Job {
Call(Box<CallRecord>),
Event(EventJob),
}
impl From<EventJob> for Job {
fn from(job: EventJob) -> Self {
Self::Event(job)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Kind {
Flush,
Shutdown,
}
#[derive(Debug)]
pub(super) enum Reply {
Async(oneshot::Sender<bool>),
Blocking(mpsc::Sender<bool>),
Nobody,
}
impl Reply {
pub(super) fn send(self, answer: bool) {
match self {
Self::Async(sender) => {
let _ = sender.send(answer);
}
Self::Blocking(sender) => {
let _ = sender.send(answer);
}
Self::Nobody => {}
}
}
}
#[derive(Debug)]
pub(super) struct Request {
pub(super) kind: Kind,
pub(super) deadline: Instant,
pub(super) reply: Reply,
}
impl Request {
pub(super) fn new(kind: Kind, timeout: Duration, reply: Reply) -> Self {
let now = Instant::now();
let deadline = now
.checked_add(timeout)
.or_else(|| now.checked_add(FOREVER))
.unwrap_or(now);
Self {
kind,
deadline,
reply,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Worker {
Idle,
Starting,
Running(ThreadId),
Gone,
}
#[derive(Debug)]
pub(super) struct Queue {
pub(super) pending: VecDeque<Job>,
pub(super) lines: Vec<String>,
pub(super) requests: Vec<Request>,
pub(super) in_flight: usize,
pub(super) shutting_down: bool,
pub(super) closed: bool,
pub(super) worker: Worker,
}
impl Queue {
pub(super) fn new() -> Self {
Self {
pending: VecDeque::new(),
lines: Vec::new(),
requests: Vec::new(),
in_flight: 0,
shutting_down: false,
closed: false,
worker: Worker::Idle,
}
}
fn takes_lines(&self) -> bool {
match self.worker {
Worker::Idle => !self.shutting_down,
Worker::Starting | Worker::Running(_) => true,
Worker::Gone => false,
}
}
pub(super) fn push_line(&mut self, line: String) {
if self.takes_lines() {
self.lines.push(line);
}
}
pub(super) fn push_job(&mut self, job: Job, max_pending: usize) -> Option<Job> {
if self.shutting_down {
self.push_line(SHUTTING_DOWN.to_owned());
return Some(job);
}
if self.worker == Worker::Gone {
return Some(job);
}
let dropped = if self.pending.len() >= max_pending {
self.push_line(QUEUE_FULL.to_owned());
self.pending.pop_front()
} else {
None
};
self.pending.push_back(job);
dropped
}
pub(super) fn claim_start(&mut self) -> bool {
let has_work = !self.pending.is_empty() || !self.lines.is_empty();
if self.worker == Worker::Idle && has_work {
self.worker = Worker::Starting;
return true;
}
false
}
pub(super) fn push_request(&mut self, request: Request) -> Option<bool> {
if request.kind == Kind::Shutdown {
self.shutting_down = true;
}
match self.worker {
Worker::Idle => Some(true),
Worker::Gone => Some(false),
Worker::Starting | Worker::Running(_) => {
self.requests.push(request);
None
}
}
}
pub(super) fn close(&mut self, drain: Duration, answer: mpsc::Sender<bool>) -> bool {
self.closed = true;
let on_worker = self.worker == Worker::Running(thread::current().id());
let reply = if on_worker {
Reply::Nobody
} else {
Reply::Blocking(answer)
};
let queued = self
.push_request(Request::new(Kind::Shutdown, drain, reply))
.is_none();
queued && !on_worker
}
pub(super) fn take_all(&mut self) -> (VecDeque<Job>, Vec<Request>) {
self.worker = Worker::Gone;
self.lines.clear();
(mem::take(&mut self.pending), mem::take(&mut self.requests))
}
}