use crate::error::Result;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicUsize, Ordering};
use tokio::runtime::{Builder, Runtime};
use tokio::sync::{Semaphore, SemaphorePermit};
static RUNTIME: OnceLock<Runtime> = OnceLock::new();
pub fn install_panic_logging() {
static INSTALLED: OnceLock<()> = OnceLock::new();
INSTALLED.get_or_init(|| {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let location = info
.location()
.map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column()))
.unwrap_or_else(|| "<unknown location>".to_string());
let msg = info
.payload()
.downcast_ref::<&str>()
.copied()
.or_else(|| info.payload().downcast_ref::<String>().map(|s| s.as_str()))
.unwrap_or("<non-string panic payload>");
let thread = std::thread::current();
let thread_name = thread.name().unwrap_or("<unnamed>");
eprintln!(
"rustuya: PANIC on thread '{}' at {} — {}\nbacktrace:\n{}",
thread_name,
location,
msg,
std::backtrace::Backtrace::force_capture()
);
prev(info);
}));
});
}
static CONNECT_SEM: OnceLock<Semaphore> = OnceLock::new();
static CONNECT_LIMIT: AtomicUsize = AtomicUsize::new(0);
pub fn set_connect_concurrency(n: usize) -> bool {
let n = n.max(1);
let installed = CONNECT_SEM.set(Semaphore::new(n)).is_ok();
if installed {
CONNECT_LIMIT.store(n, Ordering::Relaxed);
}
installed
}
pub fn connect_concurrency() -> usize {
CONNECT_LIMIT.load(Ordering::Relaxed)
}
pub(crate) async fn connect_permit() -> Option<SemaphorePermit<'static>> {
match CONNECT_SEM.get() {
Some(sem) => Some(
sem.acquire()
.await
.expect("rustuya: connect semaphore is never closed"),
),
None => None,
}
}
pub fn maximize_fd_limit() -> Result<()> {
#[cfg(unix)]
{
use crate::error::TuyaError;
use log::info;
let (soft, hard) = rlimit::getrlimit(rlimit::Resource::NOFILE)
.map_err(|e| TuyaError::io_other(format!("Failed to get rlimit: {e}")))?;
if soft < hard {
rlimit::setrlimit(rlimit::Resource::NOFILE, hard, hard)
.map_err(|e| TuyaError::io_other(format!("Failed to set rlimit: {e}")))?;
info!("File descriptor limit increased from {soft} to {hard}");
}
}
Ok(())
}
pub(crate) fn get_runtime() -> &'static Runtime {
RUNTIME.get_or_init(|| {
Builder::new_multi_thread()
.enable_all()
.build()
.expect("rustuya: failed to construct background tokio runtime")
})
}
pub(crate) fn spawn<F>(future: F) -> tokio::task::JoinHandle<()>
where
F: std::future::Future<Output = ()> + Send + 'static,
{
get_runtime().spawn(future)
}