Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple Clang instances per thread. #46

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -45,4 +49,4 @@ harness = true

[package.metadata.docs.rs]

features = ["clang_10_0"]
features = ["clang_14_0"]
88 changes: 50 additions & 38 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod token;

pub mod sonar;

use std::cell::Cell;
use std::cmp;
use std::fmt;
use std::hash;
Expand All @@ -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::*;

Expand Down Expand Up @@ -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))`
Expand Down Expand Up @@ -1616,62 +1616,74 @@ impl Visibility {

type PhantomUnsendUnsync = PhantomData<*mut ()>;

static AVAILABLE: AtomicBool = AtomicBool::new(true);
thread_local! {
static CLANG_ACTIVE: Cell<bool> = 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<std::sync::Arc<clang_sys::SharedLibrary>>,
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<Clang, String> {
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<Clang, String> {
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();
}
}
}
}

Expand Down