use std::{
marker::PhantomData,
sync::atomic::{AtomicUsize, Ordering},
};
use cares_sys as sys;
use crate::{AresError, Result};
const TRANSITION: usize = usize::MAX;
static LIBRARY_USERS: AtomicUsize = AtomicUsize::new(0);
pub struct LibraryGuard {
_not_send: PhantomData<*mut ()>,
}
impl LibraryGuard {
pub fn acquire() -> Result<Self> {
loop {
let users = LIBRARY_USERS.load(Ordering::Acquire);
if users == TRANSITION {
std::thread::yield_now();
continue;
}
if users == TRANSITION - 1 {
return Err(AresError::invalid_input(
"library reference count exhausted",
));
}
let next = if users == 0 { TRANSITION } else { users + 1 };
if LIBRARY_USERS
.compare_exchange_weak(users, next, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
continue;
}
if users == 0 {
let status = unsafe { sys::ares_library_init(sys::ARES_LIB_INIT_ALL) };
if status != 0 {
LIBRARY_USERS.store(0, Ordering::Release);
return Err(AresError::from_status(status));
}
if unsafe { sys::ares_threadsafety() } != sys::ares_bool_t::ARES_FALSE {
unsafe { sys::ares_library_cleanup() };
LIBRARY_USERS.store(0, Ordering::Release);
return Err(AresError::ThreadSafetyEnabled);
}
LIBRARY_USERS.store(1, Ordering::Release);
}
return Ok(Self {
_not_send: PhantomData,
});
}
}
}
impl Drop for LibraryGuard {
fn drop(&mut self) {
loop {
let users = LIBRARY_USERS.load(Ordering::Acquire);
let next = if users == 1 { TRANSITION } else { users - 1 };
if LIBRARY_USERS
.compare_exchange_weak(users, next, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
continue;
}
if users == 1 {
unsafe { sys::ares_library_cleanup() };
LIBRARY_USERS.store(0, Ordering::Release);
}
return;
}
}
}