Struct 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

Source

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?
examples/example.rs (lines 47-52)
39fn main() {
40    let mut plugs = Plugins {
41        plugins: Vec::new(),
42    };
43
44    // Setup the reload handler. A temporary directory will be created inside the target/debug
45    // where plugins will be loaded from. That is because on some OS:es loading a shared lib
46    // will lock the file so we can't overwrite it so this works around that issue.
47    let mut reload_handler = DynamicReload::new(
48        Some(vec!["target/debug"]),
49        Some("target/debug"),
50        Search::Default,
51        Duration::from_secs(2),
52    );
53
54    // test_shared is generated in build.rs
55    match unsafe { reload_handler.add_library("test_shared", PlatformName::Yes) } {
56        Ok(lib) => plugs.add_plugin(&lib),
57        Err(e) => {
58            println!("Unable to load dynamic lib, err {:?}", e);
59            return;
60        }
61    }
62
63    // While this is running (printing a constant number) change return value in file src/test_shared.rs
64    // build the project with cargo build and notice that this code will now return the new value
65    loop {
66        unsafe {
67            reload_handler.update(&Plugins::reload_callback, &mut plugs);
68        }
69
70        if plugs.plugins.len() > 0 {
71            // In a real program you want to cache the symbol and not do it every time if your
72            // application is performance critical
73            let fun: Symbol<extern "C" fn() -> i32> =
74                unsafe { plugs.plugins[0].lib.get(b"shared_fun\0").unwrap() };
75
76            println!("Value {}", fun());
77        }
78
79        // Wait for 0.5 sec
80        thread::sleep(Duration::from_millis(500));
81    }
82}
Source

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?
examples/example.rs (line 55)
39fn main() {
40    let mut plugs = Plugins {
41        plugins: Vec::new(),
42    };
43
44    // Setup the reload handler. A temporary directory will be created inside the target/debug
45    // where plugins will be loaded from. That is because on some OS:es loading a shared lib
46    // will lock the file so we can't overwrite it so this works around that issue.
47    let mut reload_handler = DynamicReload::new(
48        Some(vec!["target/debug"]),
49        Some("target/debug"),
50        Search::Default,
51        Duration::from_secs(2),
52    );
53
54    // test_shared is generated in build.rs
55    match unsafe { reload_handler.add_library("test_shared", PlatformName::Yes) } {
56        Ok(lib) => plugs.add_plugin(&lib),
57        Err(e) => {
58            println!("Unable to load dynamic lib, err {:?}", e);
59            return;
60        }
61    }
62
63    // While this is running (printing a constant number) change return value in file src/test_shared.rs
64    // build the project with cargo build and notice that this code will now return the new value
65    loop {
66        unsafe {
67            reload_handler.update(&Plugins::reload_callback, &mut plugs);
68        }
69
70        if plugs.plugins.len() > 0 {
71            // In a real program you want to cache the symbol and not do it every time if your
72            // application is performance critical
73            let fun: Symbol<extern "C" fn() -> i32> =
74                unsafe { plugs.plugins[0].lib.get(b"shared_fun\0").unwrap() };
75
76            println!("Value {}", fun());
77        }
78
79        // Wait for 0.5 sec
80        thread::sleep(Duration::from_millis(500));
81    }
82}
Source

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?
examples/example.rs (line 67)
39fn main() {
40    let mut plugs = Plugins {
41        plugins: Vec::new(),
42    };
43
44    // Setup the reload handler. A temporary directory will be created inside the target/debug
45    // where plugins will be loaded from. That is because on some OS:es loading a shared lib
46    // will lock the file so we can't overwrite it so this works around that issue.
47    let mut reload_handler = DynamicReload::new(
48        Some(vec!["target/debug"]),
49        Some("target/debug"),
50        Search::Default,
51        Duration::from_secs(2),
52    );
53
54    // test_shared is generated in build.rs
55    match unsafe { reload_handler.add_library("test_shared", PlatformName::Yes) } {
56        Ok(lib) => plugs.add_plugin(&lib),
57        Err(e) => {
58            println!("Unable to load dynamic lib, err {:?}", e);
59            return;
60        }
61    }
62
63    // While this is running (printing a constant number) change return value in file src/test_shared.rs
64    // build the project with cargo build and notice that this code will now return the new value
65    loop {
66        unsafe {
67            reload_handler.update(&Plugins::reload_callback, &mut plugs);
68        }
69
70        if plugs.plugins.len() > 0 {
71            // In a real program you want to cache the symbol and not do it every time if your
72            // application is performance critical
73            let fun: Symbol<extern "C" fn() -> i32> =
74                unsafe { plugs.plugins[0].lib.get(b"shared_fun\0").unwrap() };
75
76            println!("Value {}", fun());
77        }
78
79        // Wait for 0.5 sec
80        thread::sleep(Duration::from_millis(500));
81    }
82}

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.