diff --git a/Cargo.toml b/Cargo.toml index 433aead0d..d00fb49d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,10 @@ clang_7_0 = ["clang-sys/clang_7_0", "clang_6_0"] clang_8_0 = ["clang-sys/clang_8_0", "clang_7_0"] clang_9_0 = ["clang-sys/clang_9_0", "clang_8_0"] clang_10_0 = ["clang-sys/clang_10_0", "clang_9_0"] +clang_11_0 = ["clang-sys/clang_11_0", "clang_10_0"] +clang_12_0 = ["clang-sys/clang_12_0", "clang_11_0"] +clang_13_0 = ["clang-sys/clang_13_0", "clang_12_0"] +clang_14_0 = ["clang-sys/clang_14_0", "clang_13_0"] runtime = ["clang-sys/runtime"] static = ["clang-sys/static"] @@ -45,4 +49,4 @@ harness = true [package.metadata.docs.rs] -features = ["clang_10_0"] +features = ["clang_14_0"] diff --git a/src/lib.rs b/src/lib.rs index dd37c7c14..dd90b7065 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub mod token; pub mod sonar; +use std::cell::Cell; use std::cmp; use std::fmt; use std::hash; @@ -43,7 +44,6 @@ use std::convert::TryInto; use std::ffi::{CString}; use std::marker::{PhantomData}; use std::path::{Path, PathBuf}; -use std::sync::atomic::{self, AtomicBool}; use clang_sys::*; @@ -769,7 +769,7 @@ pub enum EntityKind { /// `__attribute__((clang::convergent))` /// /// Only produced by `libclang` 9.0 and later. - ConvergentAttr = 438, + ConvergentAttr = 438, /// Only produced by `libclang` 9.0 and later. WarnUnusedAttr = 439, /// `__attribute__((nodiscard))` @@ -1616,62 +1616,74 @@ impl Visibility { type PhantomUnsendUnsync = PhantomData<*mut ()>; -static AVAILABLE: AtomicBool = AtomicBool::new(true); +thread_local! { + static CLANG_ACTIVE: Cell = Cell::new(false); +} /// An empty type which prevents the use of this library from multiple threads simultaneously. #[derive(Debug)] -pub struct Clang(PhantomUnsendUnsync); +#[non_exhaustive] +pub struct Clang { + #[cfg(feature = "runtime")] + libclang: Option>, + unsend_unsync: PhantomUnsendUnsync, +} impl Clang { //- Constructors ----------------------------- - /// Constructs a new `Clang`. + /// Constructs a new `Clang` instance. /// - /// Only one instance of `Clang` is allowed at a time. + /// Only one `Clang` instance is allowed per thread. /// /// # Failures /// - /// * an instance of `Clang` already exists - /// * a `libclang` shared library could not be found - /// * a `libclang` shared library symbol could not be loaded - #[cfg(feature="runtime")] + /// * a `Clang` instance already exists for this thread + #[cfg_attr(feature = "runtime", doc = "* a `libclang` shared library could not be found")] + #[cfg_attr(feature = "runtime", doc = "* a `libclang` shared library symbol could not be loaded")] pub fn new() -> Result { - if AVAILABLE.swap(false, atomic::Ordering::SeqCst) { - load().map(|_| Clang(PhantomData)) - } else { - Err("an instance of `Clang` already exists".into()) - } - } + CLANG_ACTIVE.with(|clang_active| { + if clang_active.get() { + Err("an instance of `Clang` already exists".to_string()) + } else { + clang_active.set(true); + Ok(()) + } + })?; - /// Constructs a new `Clang`. - /// - /// Only one instance of `Clang` is allowed at a time. - /// - /// # Failures - /// - /// * an instance of `Clang` already exists - #[cfg(not(feature="runtime"))] - pub fn new() -> Result { - if AVAILABLE.swap(false, atomic::Ordering::SeqCst) { - Ok(Clang(PhantomData)) - } else { - Err("an instance of `Clang` already exists".into()) + #[cfg(feature = "runtime")] + { + if !clang_sys::is_loaded() { + clang_sys::load()?; + } } - } -} -#[cfg(feature="runtime")] -impl Drop for Clang { - fn drop(&mut self) { - unload().unwrap(); - AVAILABLE.store(true, atomic::Ordering::SeqCst); + Ok(Clang { + #[cfg(feature = "runtime")] + libclang: Some(clang_sys::get_library().unwrap()), + unsend_unsync: PhantomData, + }) } } -#[cfg(not(feature="runtime"))] impl Drop for Clang { fn drop(&mut self) { - AVAILABLE.store(true, atomic::Ordering::SeqCst); + CLANG_ACTIVE.with(|clang_active| { + clang_active.set(false); + }); + + #[cfg(feature = "runtime")] + { + // Drop the contained reference so the `unload` call below actually + // unloads the the last `libclang` instance. + let libclang = self.libclang.take().unwrap(); + let unload = std::sync::Arc::strong_count(&libclang) == 1; + drop(libclang); + + if unload { + let _ = clang_sys::unload(); + } + } } }