use std::sync::{Condvar, Mutex, PoisonError};
use std::time::Instant;
use crate::errors::Error;
use crate::transport::common::{lock, SessionState};
#[derive(Debug)]
pub(crate) struct ConnectionSignal {
state: Mutex<SessionState>,
changed: Condvar,
}
impl Default for ConnectionSignal {
fn default() -> Self {
Self {
state: Mutex::new(SessionState::Connected),
changed: Condvar::new(),
}
}
}
impl ConnectionSignal {
pub(crate) fn is_connected(&self) -> bool {
*lock(&self.state) == SessionState::Connected
}
pub(crate) fn set_connected(&self) {
self.set(SessionState::Connected);
}
pub(crate) fn set_disconnected(&self) {
self.set(SessionState::Disconnected);
}
pub(crate) fn shutdown(&self) {
self.set(SessionState::ShutDown);
}
pub(crate) fn fail(&self) {
self.set(SessionState::Failed);
}
pub(crate) fn check_ended(&self) -> Result<(), Error> {
lock(&self.state).check_ended()
}
pub(crate) fn check(&self) -> Result<(), Error> {
lock(&self.state).check()
}
pub(crate) fn wait_connected(&self, deadline: Option<Instant>) -> Result<(), Error> {
let mut state = lock(&self.state);
while *state == SessionState::Disconnected {
state = match deadline {
None => self.changed.wait(state).unwrap_or_else(PoisonError::into_inner),
Some(deadline) => {
let remaining = deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
return Err(Error::Timeout);
}
self.changed.wait_timeout(state, remaining).unwrap_or_else(PoisonError::into_inner).0
}
};
}
state.check()
}
fn set(&self, new_state: SessionState) {
let mut state = lock(&self.state);
if state.is_terminal() {
return;
}
*state = new_state;
self.changed.notify_all();
}
}
#[cfg(test)]
#[path = "connection_signal_tests.rs"]
mod tests;