From ff82786f57ac6f7dc730955a86c4872dd2cbf77b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 13 Dec 2016 15:44:19 +0100 Subject: [PATCH] link: Improvements over the runtime API. This allows the runtime API: * To be queried. * To be reused across threads. I need the first to use it on bindgen. The second is not needed right now, but it's nice to have. The querying API is really similar to what the `gl` bindings crate uses. I wonder why a mutex to guard the setting of a thread-local variable, is there something I missed? --- src/link.rs | 138 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/src/link.rs b/src/link.rs index 4cf5b6414..af3983fc2 100644 --- a/src/link.rs +++ b/src/link.rs @@ -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 $ret)*>,)+ } impl Default for Functions { @@ -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 { @@ -75,13 +66,40 @@ macro_rules! link { } } - lazy_static!(static ref LOADED: Mutex = Mutex::new(false);); - thread_local!(static LIBRARY: RefCell> = RefCell::new(None)); + thread_local!(static LIBRARY: RefCell>> = 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. /// @@ -89,30 +107,46 @@ macro_rules! link { /// /// * 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> { + 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>) -> Option> { + 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 { 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. @@ -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()) + } } ) }