Skip to content
New issue

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

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

Already on GitHub? # to your account

link: Improvements over the runtime API. #45

Merged
merged 1 commit into from
Dec 14, 2016
Merged
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
138 changes: 84 additions & 54 deletions src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,29 @@

#[cfg(feature="runtime")]
macro_rules! link {
(@IMPL: #[cfg($cfg:meta)] fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*) => (
(@LOAD: #[cfg($cfg:meta)] fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*) => (
#[cfg($cfg)]
pub fn $name(library: &mut super::SharedLibrary) -> Result<(), String> {
let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.map_err(|_| {
format!("could not load `{}`", stringify!($name))
});
library.functions.$name = *try!(symbol);
Ok(())
pub fn $name(library: &mut super::SharedLibrary) {
let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok();
library.functions.$name = symbol.map(|s| *s);
}

#[cfg(not($cfg))]
pub fn $name(_: &mut super::SharedLibrary) -> Result<(), String> {
Ok(())
}
pub fn $name(_: &mut super::SharedLibrary) {}
);

(@IMPL: fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*) => (
pub fn $name(library: &mut super::SharedLibrary) -> Result<(), String> {
let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.map_err(|_| {
format!("could not load `{}`", stringify!($name))
});
library.functions.$name = *try!(symbol);
Ok(())
}
(@LOAD: fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*) => (
link!(@LOAD: #[cfg(feature="runtime")] fn $name($($pname: $pty), *) $(-> $ret)*);
);

($($(#[cfg($cfg:meta)])* pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;)+) => (
use std::cell::{RefCell};
use std::sync::{Mutex};
use std::sync::{Arc};

#[doc(hidden)]
/// The set of functions loaded dynamically.
#[derive(Debug)]
pub struct Functions {
$($(#[cfg($cfg)])* $name: extern fn($($pname: $pty), *) $(-> $ret)*), +
$($(#[cfg($cfg)])* pub $name: Option<extern fn($($pname: $pty), *) $(-> $ret)*>,)+
}

impl Default for Functions {
Expand All @@ -61,10 +51,11 @@ macro_rules! link {
}
}

#[doc(hidden)]
/// A dynamically loaded instance of the libclang library.
#[derive(Debug)]
pub struct SharedLibrary {
library: libloading::Library,
functions: Functions,
pub functions: Functions,
}

impl SharedLibrary {
Expand All @@ -75,44 +66,87 @@ macro_rules! link {
}
}

lazy_static!(static ref LOADED: Mutex<bool> = Mutex::new(false););
thread_local!(static LIBRARY: RefCell<Option<SharedLibrary>> = RefCell::new(None));
thread_local!(static LIBRARY: RefCell<Option<Arc<SharedLibrary>>> = RefCell::new(None));

/// Whether `libclang` is loaded on this thread.
pub fn is_loaded() -> bool {
LIBRARY.with(|l| l.borrow().is_some())
}

$(
$(#[cfg($cfg)])* pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
let f = LIBRARY.with(|l| l.borrow().as_ref().map(|l| {
match l.functions.$name {
Some(f) => f,
None => panic!("Function not loaded: {}!", stringify!($name)),
}
}));
(f.expect("a `libclang` shared library was not loaded on this thread"))($($pname), *)
}

$(#[cfg($cfg)])* pub mod $name {
use super::LIBRARY;
pub fn is_loaded() -> bool {
LIBRARY.with(|l| l.borrow().as_ref().map_or(false, |l| {
l.functions.$name.is_some()
}))
}
}
)+

$($(#[cfg($cfg)])* pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
let f = LIBRARY.with(|l| l.borrow().as_ref().map(|l| l.functions.$name));
(f.expect("a `libclang` shared library was not loaded on this thread"))($($pname), *)
})+
#[path="../build.rs"]
mod build;

mod load {
$(link!(@LOAD: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+
}

/// Loads a `libclang` shared library for use in the current thread.
///
/// # Failures
///
/// * a `libclang` shared library has already been loaded
/// * a `libclang` shared library could not be found
/// * a `libclang` shared library symbol could not be loaded
///
/// Note that this tries to find all the symbols. To check if a symbol
/// has been found or not, you can use `clang_Foo::is_loaded()`.
#[allow(dead_code)]
pub fn load() -> Result<(), String> {
#[path="../build.rs"]
mod build;
let lib = Arc::new(try!(load_manually()));
LIBRARY.with(|l| *l.borrow_mut() = Some(lib));
Ok(())
}

mod load {
$(link!(@IMPL: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+
}
/// Gets the library from tls. This, along with `set_library`, allows
/// reusing the same library across threads.
pub fn get_library() -> Option<Arc<SharedLibrary>> {
LIBRARY.with(|l| {
l.borrow_mut().clone()
})
}

let mut loaded = LOADED.lock().unwrap();
if *loaded {
return Err("a `libclang` shared library has already been loaded".into());
}
/// Sets the current library from tls, and returns the previous one.
pub fn set_library(lib: Option<Arc<SharedLibrary>>) -> Option<Arc<SharedLibrary>> {
LIBRARY.with(|l| {
let mut l = l.borrow_mut();
mem::replace(&mut *l, lib)
})
}

/// Tries to load a libclang library manually, returning the
/// corresponding `SharedLibrary`.
///
/// Only returns an error when the library couldn't be found or opened,
/// and the caller is responsible handle the functions manually.
pub fn load_manually() -> Result<SharedLibrary, String> {
let file = try!(build::find_shared_library());
let library = libloading::Library::new(&file).map_err(|_| {
format!("'{}' could not be opened", file.display())
});
let mut library = SharedLibrary::new(try!(library));
$(try!(load::$name(&mut library));)+
LIBRARY.with(|l| *l.borrow_mut() = Some(library));
*loaded = true;
Ok(())
$(load::$name(&mut library);)+

Ok(library)
}

/// Unloads the `libclang` shared library in use in the current thread.
Expand All @@ -121,17 +155,13 @@ macro_rules! link {
///
/// * a `libclang` shared library is not in use in the current thread
pub fn unload() -> Result<(), String> {
let mut loaded = LOADED.lock().unwrap();
LIBRARY.with(|l| {
let mut library = l.borrow_mut();
if library.is_some() {
*library = None;
*loaded = false;
Ok(())
} else {
Err("a `libclang` shared library is not in use in the current thread".into())
}
})
let l = set_library(None);

if l.is_some() {
Ok(())
} else {
Err("a `libclang` shared library is not in use in the current thread".into())
}
}
)
}
Expand Down