1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/// We are going to handle the global init+cleanup of libxml in tree, for now. use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; use c_signatures::*; static mut LIBXML_OBJECTS: i64 = 0; /// Initializes the global libxml2 context once, before the first libxml2 object allocation pub fn _libxml_global_init() { with_lock(|| unsafe { if LIBXML_OBJECTS == 0 { xmlInitParser(); xmlInitGlobals(); } LIBXML_OBJECTS += 1; } ); } /// Currently a no-op use `force_global_drop` instead. pub fn _libxml_global_drop() { with_lock(|| unsafe { if LIBXML_OBJECTS == 1 { // Far from perfect, more "desperate" than anything... // The big issue here is taht calling these deallocations causes segfaults // if any thread is still using libxml2 constructs. And Rust doesn't give us a good hook for "end of static scope", // hence we're sunk... For now uncommenting here and leaving a "libxml_force_global_drop()" for manual use... // ... very unsatisfying //xmlCleanupGlobals(); //xmlCleanupParser(); } LIBXML_OBJECTS -= 1; } ); } /// Forces a drop of global libxml2 context. Only invoke after all threads using libxml2 are finished. pub fn force_global_drop() { unsafe { xmlCleanupGlobals(); xmlCleanupParser(); } } /// Helper lock mechanism, to allow for safe global libxml2 init/drop fn with_lock<F>(thunk: F) where F: Fn() { static LIBXML_LOCK: AtomicBool = ATOMIC_BOOL_INIT; while LIBXML_LOCK.compare_and_swap(false, true, Ordering::SeqCst) {} thunk(); LIBXML_LOCK.store(false, Ordering::SeqCst); }