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)
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));
    }
}
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)
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));
    }
}
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)
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));
    }
}

Auto Trait Implementations§

Blanket Implementations§

source§

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

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

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

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

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

const: unstable · source§

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

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

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

const: unstable · 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 Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

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

Performs the conversion.
source§

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

§

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

The type returned in the event of a conversion error.
const: unstable · source§

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

Performs the conversion.