use std::{
ffi::c_void,
mem::{size_of, size_of_val},
ptr::{null, null_mut},
};
use libc::{
PTHREAD_MUTEX_INITIALIZER, free, malloc, memset, pthread_cond_destroy, pthread_cond_init,
pthread_cond_signal, pthread_cond_t, pthread_cond_wait, pthread_getspecific,
pthread_key_create, pthread_key_delete, pthread_key_t, pthread_mutex_destroy,
pthread_mutex_init, pthread_mutex_lock, pthread_mutex_t, pthread_mutex_unlock, pthread_self,
pthread_setspecific, pthread_t,
};
use crate::libxml::globals::XmlGlobalStatePtr;
use super::globals::XmlGlobalState;
unsafe extern "C" {
fn pthread_equal(t1: pthread_t, t2: pthread_t) -> i32;
}
pub(crate) const XML_IS_THREADED: bool = true;
pub(crate) const XML_IS_NEVER_THREADED: bool = false;
pub type XmlMutexPtr = *mut XmlMutex;
pub struct XmlMutex {
pub(crate) lock: pthread_mutex_t,
}
pub type XmlRMutexPtr = *mut XmlRMutex;
pub struct XmlRMutex {
lock: pthread_mutex_t,
held: u32,
waiters: u32,
tid: pthread_t,
cv: pthread_cond_t,
}
pub(crate) static mut GLOBALKEY: pthread_key_t = 0;
pub(crate) static mut MAINTHREAD: pthread_t = 0;
pub(crate) static mut GLOBAL_INIT_LOCK: pthread_mutex_t = PTHREAD_MUTEX_INITIALIZER;
#[doc(alias = "xmlNewMutex")]
pub unsafe extern "C" fn xml_new_mutex() -> XmlMutexPtr {
unsafe {
let tok: XmlMutexPtr = malloc(size_of::<XmlMutex>()) as _;
if tok.is_null() {
return null_mut();
}
xml_init_mutex(tok);
tok
}
}
#[doc(alias = "xmlMutexLock")]
pub unsafe extern "C" fn xml_mutex_lock(tok: XmlMutexPtr) {
unsafe {
if tok.is_null() {
return;
}
if XML_IS_THREADED {
pthread_mutex_lock(&mut (*tok).lock as _);
}
}
}
#[doc(alias = "xmlMutexUnlock")]
pub unsafe extern "C" fn xml_mutex_unlock(tok: XmlMutexPtr) {
unsafe {
if tok.is_null() {
return;
}
if XML_IS_THREADED {
pthread_mutex_unlock(&mut (*tok).lock as _);
}
}
}
#[doc(alias = "xmlFreeMutex")]
pub unsafe extern "C" fn xml_free_mutex(tok: XmlMutexPtr) {
unsafe {
if tok.is_null() {
return;
}
xml_cleanup_mutex(tok);
free(tok as _);
}
}
#[doc(alias = "xmlNewRMutex")]
pub unsafe extern "C" fn xml_new_rmutex() -> XmlRMutexPtr {
unsafe {
let tok: XmlRMutexPtr = malloc(size_of::<XmlRMutex>()) as _;
if tok.is_null() {
return null_mut();
}
if !XML_IS_NEVER_THREADED {
pthread_mutex_init(&mut (*tok).lock as _, null_mut());
(*tok).held = 0;
(*tok).waiters = 0;
pthread_cond_init(&mut (*tok).cv as _, null_mut());
}
tok
}
}
#[doc(alias = "xmlRMutexLock")]
pub unsafe extern "C" fn xml_rmutex_lock(tok: XmlRMutexPtr) {
unsafe {
if tok.is_null() {
return;
}
if !XML_IS_THREADED {
return;
}
pthread_mutex_lock(&mut (*tok).lock as _);
if (*tok).held != 0 {
if pthread_equal((*tok).tid, pthread_self()) != 0 {
(*tok).held += 1;
pthread_mutex_unlock(&mut (*tok).lock as _);
return;
} else {
(*tok).waiters += 1;
while (*tok).held != 0 {
pthread_cond_wait(&mut (*tok).cv as _, &mut (*tok).lock as _);
}
(*tok).waiters -= 1;
}
}
(*tok).tid = pthread_self();
(*tok).held = 1;
pthread_mutex_unlock(&mut (*tok).lock as _);
}
}
#[doc(alias = "xmlRMutexUnlock")]
pub unsafe extern "C" fn xml_rmutex_unlock(tok: XmlRMutexPtr) {
unsafe {
if tok.is_null() {
return;
}
if !XML_IS_THREADED {
return;
}
pthread_mutex_lock(&mut (*tok).lock as _);
(*tok).held -= 1;
if (*tok).held == 0 {
if (*tok).waiters != 0 {
pthread_cond_signal(&mut (*tok).cv as _);
}
memset(&raw mut (*tok).tid as _, 0, size_of_val(&(*tok).tid));
}
pthread_mutex_unlock(&mut (*tok).lock as _);
}
}
#[doc(alias = "xmlFreeRMutex")]
pub unsafe extern "C" fn xml_free_rmutex(tok: XmlRMutexPtr) {
unsafe {
if tok.is_null() {
return;
}
if !XML_IS_NEVER_THREADED {
pthread_mutex_destroy(&mut (*tok).lock as _);
pthread_cond_destroy(&mut (*tok).cv as _);
}
free(tok as _);
}
}
#[doc(alias = "xmlNewGlobalState")]
unsafe extern "C" fn xml_new_global_state() -> XmlGlobalStatePtr {
unsafe {
use crate::generic_error;
let gs: *mut XmlGlobalState = malloc(size_of::<XmlGlobalState>()) as _;
if gs.is_null() {
generic_error!("xmlGetGlobalState: out of memory\n");
return null_mut();
}
memset(gs as _, 0, size_of::<XmlGlobalState>());
gs
}
}
#[doc(alias = "xmlGetGlobalState")]
pub(crate) unsafe extern "C" fn xml_get_global_state() -> XmlGlobalStatePtr {
unsafe {
if !XML_IS_THREADED {
return null_mut();
}
let globalval: *mut XmlGlobalState = pthread_getspecific(GLOBALKEY) as *mut XmlGlobalState;
if globalval.is_null() {
let tsd: *mut XmlGlobalState = xml_new_global_state();
if tsd.is_null() {
return null_mut();
}
pthread_setspecific(GLOBALKEY, tsd as _);
return tsd;
}
globalval
}
}
#[doc(alias = "xmlGlobalInitMutexLock")]
pub(crate) unsafe extern "C" fn __xml_global_init_mutex_lock() {
unsafe {
if !XML_IS_THREADED {
return;
}
pthread_mutex_lock(&raw mut GLOBAL_INIT_LOCK);
}
}
pub(crate) unsafe extern "C" fn __xml_global_init_mutex_unlock() {
unsafe {
if !XML_IS_THREADED {
return;
}
pthread_mutex_unlock(&raw mut GLOBAL_INIT_LOCK);
}
}
#[doc(alias = "xmlGlobalInitMutexDestroy")]
pub(crate) unsafe extern "C" fn __xml_global_init_mutex_destroy() {}
#[doc(alias = "xmlFreeGlobalState")]
unsafe extern "C" fn xml_free_global_state(state: *mut c_void) {
unsafe {
let gs: *mut XmlGlobalState = state as _;
(*gs).xml_last_error.reset();
free(state);
}
}
#[doc(alias = "xmlInitThreadsInternal")]
pub(crate) unsafe extern "C" fn xml_init_threads_internal() {
unsafe {
pthread_key_create(&raw mut GLOBALKEY, Some(xml_free_global_state));
MAINTHREAD = pthread_self();
}
}
#[doc(alias = "xmlCleanupThreadsInternal")]
pub(crate) unsafe extern "C" fn xml_cleanup_threads_internal() {
unsafe {
pthread_key_delete(GLOBALKEY);
}
}
#[doc(alias = "xmlInitMutex")]
pub(crate) unsafe extern "C" fn xml_init_mutex(mutex: XmlMutexPtr) {
unsafe {
if !XML_IS_NEVER_THREADED {
pthread_mutex_init(&mut (*mutex).lock as _, null());
}
}
}
#[doc(alias = "xmlCleanupMutex")]
pub(crate) unsafe extern "C" fn xml_cleanup_mutex(mutex: XmlMutexPtr) {
unsafe {
if !XML_IS_NEVER_THREADED {
pthread_mutex_destroy(&mut (*mutex).lock as _);
}
}
}