#![cfg(any(target_os="android", target_os="l4re", target_os="linux"))]
#![doc(cfg(any(target_os="android", target_os="l4re", target_os="linux")))]
use {
alloc::sync::Arc,
core::sync::atomic::{self, AtomicBool},
std::{
io::{self, ErrorKind, Write},
os::unix::net::UnixListener,
sync::{
OnceLock,
mpsc::{self, Sender, TryRecvError},
},
thread,
},
crate::Result,
self::{
message::Message,
server::Server,
},
};
mod message;
mod server;
macro_rules! print_err { ($err: expr) => {{
{
let mut stderr = io::stderr().lock();
if stderr.write_all(__!("{err}\n", err=$err).as_bytes()).is_ok() {
if stderr.flush().is_err() {
}
}
}
}}}
pub (super) const ATOMIC_ORDERING: atomic::Ordering = atomic::Ordering::Relaxed;
static SENDER: OnceLock<Result<Sender<Message>>> = OnceLock::new();
pub (super) fn add(listener: UnixListener, clonable: bool, stop_flag: Arc<AtomicBool>) -> Result<()> {
if stop_flag.load(ATOMIC_ORDERING) {
return Err(err!(ErrorKind::InvalidData, "Invalid initial value of stop-flag"));
}
match get_or_init_sender() {
Ok(sender) => sender.send(Message::Add { listener, clonable, stop_flag }).map_err(|_| err!()),
Err(err) => Err(err!("{err}")),
}
}
fn get_or_init_sender<'a>() -> &'a Result<Sender<Message>> {
SENDER.get_or_init(|| {
match Server::make() {
Ok(mut server) => {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || loop {
if let Err(err) = server.check() {
print_err!(err);
}
match receiver.try_recv() {
Ok(Message::Add { listener, clonable, stop_flag }) => if let Err(err) = server.push(listener, clonable, stop_flag) {
print_err!(err);
},
Err(TryRecvError::Empty) => {},
Err(TryRecvError::Disconnected) => {
print_err!("Channel is down!");
break;
},
};
});
Ok(sender)
},
Err(err) => Err(err),
}
})
}