Struct dynamic_reload::DynamicReload
source · pub struct DynamicReload { /* private fields */ }
Expand description
Contains information about loaded libraries and also tracks search paths and reloading events.
Implementations§
source§impl<'a> DynamicReload
impl<'a> DynamicReload
sourcepub fn new(
search_paths: Option<Vec<&'a str>>,
shadow_dir: Option<&'a str>,
_search: Search,
debounce_duration: Duration
) -> DynamicReload
pub fn new( search_paths: Option<Vec<&'a str>>, shadow_dir: Option<&'a str>, _search: Search, debounce_duration: Duration ) -> DynamicReload
Creates a DynamicReload object.
search_path
is a list of extra paths that when
calling add_library the code will
also try to find the shared library within those locations.
shadow_dir
is a location where a temporary directory will be created to
keep a copy of all the shared libraries and load from there. The reason is that some
operating systems locks loaded shared files which would make it impossible to update them.
By having a separate directory DynamicReload will look for changes in the original path
while having them loaded from another
search
This is to allow DynamicReload to search in parent directiors from the
executable. Set this to Search::Backwards
to allow that or to Search::Default
to only allow seach in the currenty directory of the of the executable
debounce_duration
is the duration that the watcher will wait after the dynamic library
changed on disk, until it will cause a reload. (Multiple write calls could be made to the library
until it is fully written.)
Examples
// No extra search paths, temp directory in target/debug, allow search backwards
DynamicReload::new(None, Some("target/debug"), Search::Backwards, Duration::from_secs(2));
// "../.." extra search path, temp directory in target/debug, allow search backwards
DynamicReload::new(Some(vec!["../.."]), Some("target/debug"), Search::Backwards, Duration::from_secs(2));
Examples found in repository?
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
fn main() {
let mut plugs = Plugins {
plugins: Vec::new(),
};
// Setup the reload handler. A temporary directory will be created inside the target/debug
// where plugins will be loaded from. That is because on some OS:es loading a shared lib
// will lock the file so we can't overwrite it so this works around that issue.
let mut reload_handler = DynamicReload::new(
Some(vec!["target/debug"]),
Some("target/debug"),
Search::Default,
Duration::from_secs(2),
);
// test_shared is generated in build.rs
match unsafe { reload_handler.add_library("test_shared", PlatformName::Yes) } {
Ok(lib) => plugs.add_plugin(&lib),
Err(e) => {
println!("Unable to load dynamic lib, err {:?}", e);
return;
}
}
// While this is running (printing a constant number) change return value in file src/test_shared.rs
// build the project with cargo build and notice that this code will now return the new value
loop {
unsafe {
reload_handler.update(&Plugins::reload_callback, &mut plugs);
}
if plugs.plugins.len() > 0 {
// In a real program you want to cache the symbol and not do it every time if your
// application is performance critical
let fun: Symbol<extern "C" fn() -> i32> =
unsafe { plugs.plugins[0].lib.get(b"shared_fun\0").unwrap() };
println!("Value {}", fun());
}
// Wait for 0.5 sec
thread::sleep(Duration::from_millis(500));
}
}
sourcepub unsafe fn add_library(
&mut self,
name: &str,
name_format: PlatformName
) -> Result<Arc<Lib>>
pub unsafe fn add_library( &mut self, name: &str, name_format: PlatformName ) -> Result<Arc<Lib>>
Add a library to be loaded and to be reloaded once updated. If PlatformName is set to Yes the input name will be formatted according to the standard way libraries looks on that platform examples:
Windows: foobar -> foobar.dll
Linux: foobar -> libfoobar.so
Mac: foobar -> libfoobar.dylib
If set to no the given input name will be used as is. This function will also search for the file in this priority order
1. Current directory
2. In the search paths (relative to current directory)
3. Current directory of the executable
4. Search backwards from executable if Backwards has been set DynamicReload::new
Examples
// Add a library named test_lib and format it according to standard platform standard.
add_library("test_lib", PlatformName::Yes)
Safety
Note taken from libloading that is used for library loading
When a library is loaded, initialisation routines contained within it are executed. For the purposes of safety, the execution of these routines is conceptually the same calling an unknown foreign function and may impose arbitrary requirements on the caller for the call to be sound. Additionally, the callers of this function must also ensure that execution of the termination routines contained within the library is safe as well. These routines may be executed when the library is unloaded.
Examples found in repository?
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
fn main() {
let mut plugs = Plugins {
plugins: Vec::new(),
};
// Setup the reload handler. A temporary directory will be created inside the target/debug
// where plugins will be loaded from. That is because on some OS:es loading a shared lib
// will lock the file so we can't overwrite it so this works around that issue.
let mut reload_handler = DynamicReload::new(
Some(vec!["target/debug"]),
Some("target/debug"),
Search::Default,
Duration::from_secs(2),
);
// test_shared is generated in build.rs
match unsafe { reload_handler.add_library("test_shared", PlatformName::Yes) } {
Ok(lib) => plugs.add_plugin(&lib),
Err(e) => {
println!("Unable to load dynamic lib, err {:?}", e);
return;
}
}
// While this is running (printing a constant number) change return value in file src/test_shared.rs
// build the project with cargo build and notice that this code will now return the new value
loop {
unsafe {
reload_handler.update(&Plugins::reload_callback, &mut plugs);
}
if plugs.plugins.len() > 0 {
// In a real program you want to cache the symbol and not do it every time if your
// application is performance critical
let fun: Symbol<extern "C" fn() -> i32> =
unsafe { plugs.plugins[0].lib.get(b"shared_fun\0").unwrap() };
println!("Value {}", fun());
}
// Wait for 0.5 sec
thread::sleep(Duration::from_millis(500));
}
}
sourcepub unsafe fn update<F, T>(&mut self, update_call: &F, data: &mut T)where
F: Fn(&mut T, UpdateState, Option<&Arc<Lib>>),
pub unsafe fn update<F, T>(&mut self, update_call: &F, data: &mut T)where F: Fn(&mut T, UpdateState, Option<&Arc<Lib>>),
Needs to be called in order to handle reloads of libraries.
update_call
function with its data needs to be supplied to allow the application to
take appropriate action depending on what needs to be done with the loaded library.
struct Plugins {
// ...
}
impl Plugins {
fn reload_callback(&mut self, state: UpdateState, lib: Option<&Arc<Lib>>) {
match state {
UpdateState::Before => // save state, remove from lists, etc, here
UpdateState::After => // shared lib reloaded, re-add, restore state
UpdateState::ReloadFailed(Error) => // shared lib failed to reload due to error
}
}
}
fn main() {
let plugins = Plugins { ... };
let mut dr = DynamicReload::new(None, Some("target/debug"), Search::Backwards, Duration::from_secs(2));
dr.add_library("test_shared", Search::Backwards);
dr.update(Plugin::reload_callback, &mut plugins);
}
Safety
Note taken from libloading that is used for library loading
When a library is loaded, initialisation routines contained within it are executed. For the purposes of safety, the execution of these routines is conceptually the same calling an unknown foreign function and may impose arbitrary requirements on the caller for the call to be sound. Additionally, the callers of this function must also ensure that execution of the termination routines contained within the library is safe as well. These routines may be executed when the library is unloaded.
Examples found in repository?
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
fn main() {
let mut plugs = Plugins {
plugins: Vec::new(),
};
// Setup the reload handler. A temporary directory will be created inside the target/debug
// where plugins will be loaded from. That is because on some OS:es loading a shared lib
// will lock the file so we can't overwrite it so this works around that issue.
let mut reload_handler = DynamicReload::new(
Some(vec!["target/debug"]),
Some("target/debug"),
Search::Default,
Duration::from_secs(2),
);
// test_shared is generated in build.rs
match unsafe { reload_handler.add_library("test_shared", PlatformName::Yes) } {
Ok(lib) => plugs.add_plugin(&lib),
Err(e) => {
println!("Unable to load dynamic lib, err {:?}", e);
return;
}
}
// While this is running (printing a constant number) change return value in file src/test_shared.rs
// build the project with cargo build and notice that this code will now return the new value
loop {
unsafe {
reload_handler.update(&Plugins::reload_callback, &mut plugs);
}
if plugs.plugins.len() > 0 {
// In a real program you want to cache the symbol and not do it every time if your
// application is performance critical
let fun: Symbol<extern "C" fn() -> i32> =
unsafe { plugs.plugins[0].lib.get(b"shared_fun\0").unwrap() };
println!("Value {}", fun());
}
// Wait for 0.5 sec
thread::sleep(Duration::from_millis(500));
}
}