Skip to content
Open
Changes from 4 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
57 changes: 29 additions & 28 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,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 +768,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 +1615,64 @@ impl Visibility {

type PhantomUnsendUnsync = PhantomData<*mut ()>;

static AVAILABLE: AtomicBool = AtomicBool::new(true);

/// 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`.
///
/// Only one instance of `Clang` is allowed at a time.
/// Only one instance of `Clang` 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")]
#[cfg(feature = "runtime")]
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())
if !clang_sys::is_loaded() {
clang_sys::load()?;
}

let library = clang_sys::get_library().unwrap();
Ok(Clang {
libclang: Some(library),
unsend_unsync: PhantomUnsendUnsync,
})
}

/// Constructs a new `Clang`.
///
/// Only one instance of `Clang` is allowed at a time.
/// Only one instance of `Clang` is allowed per thread.
Copy link
Author

@reitermarkus reitermarkus Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I'm not actually sure if this true.

Depending on whether libclang is thread-safe or not, we either need to allow

1 Clang instance per thread where Clang: !Send + !Sync

or we need to allow

1 Clang instance per process where Clang: Send.

///
/// # Failures
///
/// * an instance of `Clang` already exists
#[cfg(not(feature="runtime"))]
#[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())
}
Ok(Clang {
unsend_unsync: PhantomUnsendUnsync,
})
}
}

#[cfg(feature="runtime")]
impl Drop for Clang {
fn drop(&mut self) {
unload().unwrap();
AVAILABLE.store(true, atomic::Ordering::SeqCst);
}
}
#[cfg(feature = "runtime")]
{
// Drop the contained reference so the `unload` call below actually
// unloads the the last `libclang` instance.
drop(self.libclang.take());

#[cfg(not(feature="runtime"))]
impl Drop for Clang {
fn drop(&mut self) {
AVAILABLE.store(true, atomic::Ordering::SeqCst);
let _ = clang_sys::unload();
}
}
}

Expand Down