use std::collections::HashMap;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{self, AssertUnwindSafe};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, OnceLock};
use crate::event::AppEvent;
use super::bindings::*;
pub static EVENT_TX: OnceLock<Mutex<HashMap<usize, std::sync::mpsc::Sender<AppEvent>>>> =
OnceLock::new();
static RE_HANDLE: OnceLock<Mutex<Option<std::thread::JoinHandle<()>>>> = OnceLock::new();
static RE_RUNNING: AtomicBool = AtomicBool::new(false);
pub struct ReThreadGuard;
impl Drop for ReThreadGuard {
fn drop(&mut self) {
unsafe { re_thread_leave() };
}
}
#[must_use]
pub fn enter_re_thread() -> ReThreadGuard {
unsafe { re_thread_enter() };
ReThreadGuard
}
pub fn on_re_thread<F: FnOnce()>(f: F) {
if !re_thread_running() {
return;
}
unsafe { re_thread_enter() };
let _guard = ReThreadGuard;
f();
}
fn re_thread_running() -> bool {
RE_RUNNING.load(Ordering::Acquire)
}
pub fn redirect_logging() {
static DBG_REDIRECTED: OnceLock<bool> = OnceLock::new();
DBG_REDIRECTED.get_or_init(|| {
unsafe extern "C" fn dbg_handler(
level: c_int,
p: *const c_char,
len: usize,
_arg: *mut c_void,
) {
let _ = panic::catch_unwind(AssertUnwindSafe(|| {
if p.is_null() || len == 0 {
return;
}
let slice = unsafe { std::slice::from_raw_parts(p as *const u8, len) };
let msg = String::from_utf8_lossy(slice);
match level {
2 => crate::rlog!(Info, "libre: {}", msg.trim()),
3 => crate::rlog!(Error, "libre: {}", msg.trim()),
4 => crate::rlog!(Warn, "libre: {}", msg.trim()),
_ => {}
}
}));
}
static mut LOG_HANDLER: log = unsafe {
log {
le: std::mem::zeroed(),
h: Some(baresip_log_handler),
}
};
unsafe extern "C" fn baresip_log_handler(level: u32, msg: *const c_char) {
let _ = panic::catch_unwind(AssertUnwindSafe(|| {
if msg.is_null() {
return;
}
let s = unsafe { std::ffi::CStr::from_ptr(msg) };
let msg = s.to_string_lossy();
match level {
0 => crate::rlog!(Debug, "baresip: {}", msg.trim()),
1 => crate::rlog!(Info, "baresip: {}", msg.trim()),
2 => crate::rlog!(Warn, "baresip: {}", msg.trim()),
3 => crate::rlog!(Error, "baresip: {}", msg.trim()),
_ => {}
}
}));
}
unsafe {
log_enable_stdout(false);
let enable_info = matches!(option_env!("RINGO_DEBUG_BARESIP"), Some("1"));
log_enable_info(enable_info);
log_enable_debug(false);
log_register_handler(&raw mut LOG_HANDLER);
let dbg_level = match option_env!("RINGO_DEBUG_BARESIP") {
Some("1") => 2, _ => 4, };
dbg_init(dbg_level, 0);
dbg_handler_set(Some(dbg_handler), std::ptr::null_mut());
}
true
});
}
pub fn start_re_thread() -> Result<(), String> {
let handle_mutex = RE_HANDLE.get_or_init(|| Mutex::new(None));
let mut guard = handle_mutex.lock().unwrap_or_else(|e| e.into_inner());
if guard.is_some() {
return Ok(()); }
let (ready_tx, ready_rx) = std::sync::mpsc::channel::<Option<String>>();
let handle = std::thread::Builder::new()
.name("baresip-re".into())
.spawn(move || unsafe {
if libre_init() != 0 {
let _ = ready_tx.send(Some("libre_init() failed".into()));
return;
}
if re_thread_async_init(4) != 0 {
let _ = ready_tx.send(Some("re_thread_async_init() failed".into()));
return;
}
let _ = ready_tx.send(None);
re_main(None);
ua_close();
module_app_unload();
conf_close();
baresip_close();
mod_close();
re_thread_async_close();
libre_close();
})
.map_err(|e| format!("failed to spawn RE thread: {e}"))?;
match ready_rx.recv() {
Ok(Some(e)) => return Err(e),
Ok(None) => {}
Err(_) => return Err("RE thread exited before init completed".into()),
}
*guard = Some(handle);
RE_RUNNING.store(true, Ordering::Release);
Ok(())
}
pub fn stop_re_thread() {
let handle_mutex = match RE_HANDLE.get() {
Some(m) => m,
None => return, };
let mut guard = handle_mutex.lock().unwrap_or_else(|e| e.into_inner());
if guard.is_none() {
return;
}
on_re_thread(|| unsafe {
ua_stop_all(true);
});
unsafe {
re_cancel();
let _ = re_thread_async(None, None, std::ptr::null_mut());
}
if let Some(handle) = guard.take() {
let _ = handle.join();
}
RE_RUNNING.store(false, Ordering::Release);
let pid = std::process::id();
let baresip_dir = format!("/tmp/ringo-baresip-{pid}");
let _ = std::fs::remove_dir_all(&baresip_dir);
}