use std::num::NonZeroUsize;
use std::sync::atomic::{AtomicUsize, Ordering};
static ACTIVE_THREAD: AtomicUsize = AtomicUsize::new(0);
#[track_caller]
pub(crate) fn check_active_thread() {
let current_thread = nonzero_thread_id();
match ACTIVE_THREAD.load(Ordering::Relaxed) {
0 => init_active_thread(current_thread),
thread_id => {
if current_thread.get() != thread_id {
thread_id_check_failed();
}
}
}
}
#[track_caller]
fn init_active_thread(tid: NonZeroUsize) {
match ACTIVE_THREAD.compare_exchange(0, tid.get(), Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => unsafe {
extern "C" fn clear_in_child() {
ACTIVE_THREAD.store(0, Ordering::Relaxed);
}
libc::pthread_atfork(None, None, Some(clear_in_child));
},
Err(_) => {
thread_id_check_failed();
}
}
}
#[cold]
#[inline(never)]
#[track_caller]
fn thread_id_check_failed() -> ! {
panic!(
"{}: postgres FFI may not not be called from multiple threads.",
std::panic::Location::caller()
);
}
fn nonzero_thread_id() -> NonZeroUsize {
std::thread_local! {
static BYTE: u8 = const { 0 };
}
BYTE.with(|p: &u8| {
let addr = sptr::Strict::addr(p as *const u8);
unsafe { NonZeroUsize::new_unchecked(addr) }
})
}