1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#[doc(hidden)]
#[macro_export]
macro_rules! link {
(
$(
unsafe extern "C" {
$(#[doc=$doc:expr])*
$(#[cfg($cfg:meta)])*
pub fn $name:ident($($pname:ident: $pty:ty),* $(,)?$(,...)?) $(-> $ret:ty)*;
}
)+
) => (
use libloading;
use std::path::PathBuf;
use std::sync::{Arc, LazyLock, RwLock};
// Wrap the loaded functions.
#[derive(Debug)]
pub(crate) struct SharedLibrary {
library: libloading::Library,
path: PathBuf,
pub functions: Functions,
}
impl SharedLibrary {
fn new(library: libloading::Library, path: PathBuf) -> Self {
Self {
library,
path,
functions: Functions::default(),
}
}
}
// `LIBRARY` holds the shared library reference.
static LIBRARY: LazyLock<RwLock<Option<Arc<SharedLibrary>>>> = LazyLock::new(|| RwLock::new(None));
// Helper function for accessing the thread-local version of the library.
fn with_library<T, F>(f: F) -> Option<T>
where
F: FnOnce(&SharedLibrary) -> T,
{
LIBRARY.read().unwrap().as_ref().map(|library| f(&library))
}
// The set of functions loaded dynamically.
#[derive(Debug, Default)]
pub(crate) struct Functions {
$(
$(#[doc=$doc])* $(#[cfg($cfg)])*
pub $name: Option<unsafe extern "C" fn($($pname: $pty), *) $(-> $ret)*>,
)+
}
// Provide functions to load each name from the shared library into the `SharedLibrary`
// struct.
mod load {
$(
$(#[cfg($cfg)])*
pub(crate) fn $name(library: &mut super::SharedLibrary) {
let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok();
library.functions.$name = match symbol {
Some(s) => *s,
None => None,
};
}
)+
}
/// Load all of the function definitions from a shared library.
///
/// If the library has already been loaded (e.g., via [`load_from`]), this is a no-op.
///
/// # Errors
///
/// May fail if the `openvino-finder` cannot discover the library on the current system.
pub fn load() -> Result<(), String> {
if LIBRARY.read().unwrap().is_some() {
return Ok(());
}
match $crate::library::find() {
None => Err("Unable to find the `openvino_c` library to load".into()),
Some(path) => load_from(path),
}
}
/// Load all of the function definitions from a shared library at the given path.
///
/// This is useful when the library is not in a standard location and cannot be
/// discovered by the `openvino-finder` search paths. The `path` should point to
/// the `openvino_c` shared library file (e.g., `libopenvino_c.so`).
///
/// If the library has already been loaded (e.g., via [`load`]), this is a no-op.
///
/// # Errors
///
/// May fail if the shared library cannot be opened or is invalid.
pub fn load_from(path: PathBuf) -> Result<(), String> {
if LIBRARY.read().unwrap().is_some() {
return Ok(());
}
let library = Arc::new(SharedLibrary::load(path)?);
*LIBRARY.write().unwrap() = Some(library);
Ok(())
}
impl SharedLibrary {
fn load(path: PathBuf) -> Result<SharedLibrary, String> {
unsafe {
let library = libloading::Library::new(&path).map_err(|e| {
format!(
"the shared library at {} could not be opened: {}",
path.display(),
e,
)
});
let mut library = SharedLibrary::new(library?, path);
$(load::$name(&mut library);)+
Ok(library)
}
}
}
// For each loaded function, we redefine them to proxy their call through the SharedLibrary
// on the local thread and into the loaded shared library implementation.
$(
$(#[doc=$doc])* $(#[cfg($cfg)])*
#[allow(clippy::missing_safety_doc)] // These are bindgen-generated functions.
pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
let f = with_library(|l| {
l.functions.$name.expect(concat!(
"`openvino_c` function not loaded: `",
stringify!($name)
))
}).expect("an `openvino_c` shared library is not loaded on this thread");
f($($pname), *)
}
)+
)
}