use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use std::sync::Arc;
use std::sync::atomic::Ordering;
use crate::outbox::Outbox;
use crate::pal::ids::ConnId;
use crate::pal::pseudoconsole::{Pseudoconsole, WindowSize};
use crate::pal::transport::Transport;
use crate::protocol::Message;
use crate::supervisor::shared::{Client, Shared, preamble_messages};
#[cfg_attr(test, mutants::skip)]
pub(crate) fn stop_clients<T: Transport, C>(shared: &Shared<T, C>) -> Option<Client<T>> {
let _attach = shared.attach();
shared.stopping.store(true, Ordering::SeqCst);
shared.client().take()
}
#[cfg_attr(test, mutants::skip)]
pub(crate) fn attach_client<T, C>(
shared: &Shared<T, C>,
conn: ConnId,
size: WindowSize,
) -> Option<u64>
where
T: Transport + Clone,
C: Pseudoconsole,
{
let attach = shared.attach();
let mut created = None;
let result = catch_unwind(AssertUnwindSafe(|| {
if shared.stopping.load(Ordering::SeqCst) {
return None;
}
let outbox = Outbox::start(shared.transport.clone(), conn);
created = Some(Arc::clone(&outbox));
let (previous, generation) = {
let mut slot = shared.client();
outbox.send(Message::Attached {
session_id: shared.session_id,
});
if let Some(held) = shared.take_preamble() {
for message in preamble_messages(&held) {
outbox.send(message);
}
}
let previous = slot.replace(Client {
conn,
outbox: Arc::clone(&outbox),
});
(previous, shared.next_attached_generation())
};
shared.note_claimed();
if let Some(old) = previous {
old.outbox.send(Message::Displaced);
old.outbox.finish();
}
_ = shared.pty_host.resize(shared.pty, size);
Some(generation)
}));
drop(attach);
match result {
Ok(Some(generation)) => Some(generation),
Ok(None) => {
shared.transport.disconnect(conn);
None
}
Err(panic) => {
if let Some(outbox) = created {
outbox.abandon();
} else {
shared.transport.disconnect(conn);
}
resume_unwind(panic)
}
}
}
#[cfg_attr(test, mutants::skip)]
pub(crate) fn apply_client_message<T, C>(
shared: &Shared<T, C>,
conn: ConnId,
message: Message,
) -> bool
where
T: Transport,
C: Pseudoconsole,
{
let slot = shared.client();
if slot.as_ref().map(|client| client.conn) != Some(conn) {
return false;
}
let result = catch_unwind(AssertUnwindSafe(|| match message {
Message::Input(data) => {
_ = shared.pty_host.write_input(shared.pty, &data);
true
}
Message::Resize { size } => {
_ = shared.pty_host.resize(shared.pty, size);
true
}
_ => false,
}));
drop(slot);
match result {
Ok(accepted) => accepted,
Err(panic) => resume_unwind(panic),
}
}
#[cfg_attr(test, mutants::skip)]
pub(crate) fn detach_client<T, C>(shared: &Shared<T, C>, conn: ConnId) -> Option<u64>
where
T: Transport + Clone,
{
let (departing, generation) = {
let mut slot = shared.client();
if slot.as_ref().map(|client| client.conn) != Some(conn) {
return None;
}
(slot.take(), shared.next_attached_generation())
};
if let Some(departing) = departing {
departing.outbox.abandon();
}
Some(generation)
}