As suggested by the dynamic_reload
crate's example, I collected Symbol
s instead of extracting them every time, but Symbol
requires a lifetime. Using a lifetime changes method signatures and breaks the compatibility with method DynamicReload::update
.
Is it a valid workaround to use std::mem::transmute
to change Symbol
's lifetime to 'static
?
extern crate dynamic_reload;
use dynamic_reload::{DynamicReload, Lib, Symbol, Search, PlatformName, UpdateState};
use std::sync::Arc;
use std::time::Duration;
use std::thread;
use std::mem::transmute;
struct Plugins {
plugins: Vec<(Arc<Lib>, Arc<Symbol<'static, extern "C" fn() -> i32>>)>,
}
impl Plugins {
fn add_plugin(&mut self, plugin: &Arc<Lib>) {
match unsafe { plugin.lib.get(b"shared_fun\0") } {
Ok(temp) => {
let f: Symbol<extern "C" fn() -> i32> = temp;
self.plugins.push((plugin.clone(), Arc::new(unsafe { transmute(f) })));
},
Err(e) => println!("Failed to load symbol: {:?}", e),
}
}
fn unload_plugins(&mut self, lib: &Arc<Lib>) {
for i in (0..self.plugins.len()).rev() {
if &self.plugins[i].0 == lib {
self.plugins.swap_remove(i);
}
}
}
fn reload_plugin(&mut self, lib: &Arc<Lib>) {
Self::add_plugin(self, lib);
}
// called when a lib needs to be reloaded.
fn reload_callback(&mut self, state: UpdateState, lib: Option<&Arc<Lib>>) {
match state {
UpdateState::Before => Self::unload_plugins(self, lib.unwrap()),
UpdateState::After => Self::reload_plugin(self, lib.unwrap()),
UpdateState::ReloadFailed(_) => println!("Failed to reload"),
}
}
}
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);
// test_shared is generated in build.rs
match 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 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 {
reload_handler.update(Plugins::reload_callback, &mut plugs);
if plugs.plugins.len() > 0 {
let fun = &plugs.plugins[0].1;
println!("Value {}", fun());
}
// Wait for 0.5 sec
thread::sleep(Duration::from_millis(500));
}
}
I still have to keep Arc<Lib>
inside the vector because Symbol
doesn't implement PartialEq
.
How to store a reference without having to deal with lifetimes?
The answer in 98% of the cases is: you don't. Lifetimes are one of the biggest reasons to use Rust. Lifetimes enforce, at compile time, that your references will always refer to something that is valid. If you wish to "ignore" lifetimes, then perhaps Rust may not the best language to realize a particular design. You may need to pick a different language or design.
Is it a valid workaround to use
std::mem::transmute
to changeSymbol
's lifetime to'static
?
transmute
is The Big Hammer, suitable for all sorts of good and bad ideas and implementations. I would encourage never using it directly, but instead wrapping it in a layer of abstraction that somehow helps you enforce the appropriate restrictions that make that particular transmute correct.
If you choose to use transmute
, you are assuming the full responsibility that the compiler previously had. It will be up to you to ensure that the reference is always valid, otherwise you are invoking undefined behavior and your program is allowed to do any number of Very Bad things.
For your specific case, you may be able to use the Rental crate to keep around "the library" and "references into the library" in a single struct that hides the lifetimes of the Symbol
s. In fact, Rental uses libloading as the motivating example and libloading powers dynamic_reload. See
Why can't I store a value and a reference to that value in the same struct? for more details and pitfalls.
I'm not optimistic that this will work because DynamicReload::update
requires a &mut self
. During that method call, it could easily invalidate all of the existing references.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With