Hi, this is a new thread that follows-up on rc::Weak extending lifetime? - #5 by godmar as advised by rc::Weak extending lifetime? - #6 by alice
I have a global table that must refer to closures that I don't want to have a 'static
lifetime. I do not want that because doing so would (in my opinion) make my entire application 'static
. To that end, I was hoping to use weak references (as in the referenced thread). As was discussed there, however, weak references do not work - the recreated object will have the same lifetime as the original object.
I'm looking for a way to - perhaps an alternative abstraction - that allows me to accomplish my goal. In the code below (link to working playground; link to playground I'd love to see working); I'd like for Application::new
to return a Application<'app>
not a Application<'static>
as it currently does.
For the example, I've wrapped Application
in a Rc<RefCell<>>
as per Ch. 15 Rust book since I need this for other parts of my (real) application, but it should not be related to the question at hand.
To make the example self-contained and motivated, I've included libc signal handling code modeled after vorner's signal handling crate but it is not related to my question.
use std::collections::HashMap;
use std::sync::Arc;
use std::os::raw::c_int;
use std::mem;
use std::{cell::RefCell, rc::Rc};
//
// GlobalData follows in part
// https://github.com/vorner/signal-hook/blob/master/signal-hook-registry/src/lib.rs
//
struct GlobalData {
table: HashMap<c_int, Arc<dyn Fn()>>
}
impl GlobalData {
fn get() -> &'static mut Self {
unsafe { GLOBAL_DATA.as_mut().unwrap() }
}
fn ensure() -> &'static mut Self {
unsafe {
GLOBAL_DATA = Some(GlobalData{
table: HashMap::new()
});
}
Self::get()
}
}
static mut GLOBAL_DATA: Option<GlobalData> = None;
fn install(signal: c_int, handler: Arc<dyn Fn()>)
{
let gdata = &mut GlobalData::ensure();
gdata.table.insert(signal, handler);
}
extern "C" fn handler(signal: c_int) {
let actiontable = &GlobalData::get().table;
match actiontable.get(&signal) {
Some(action) => action(),
None => panic!("no handler")
}
}
struct Application<'app> {
name: &'app str, // just an example of state possibly carried by Application
}
impl <'app> Application<'app> {
fn work(&self) {
println!("Alarm went off in {}", self.name);
}
// want:
fn new(name: &'app str) -> Rc<RefCell<Application<'app>>> {
// have:
//fn new(name: &'static str) -> Rc<RefCell<Application<'static>>> {
let app = Rc::new(RefCell::new(Application{
name
}));
let weak = Rc::<RefCell<Application>>::downgrade(&app.clone());
let signalhandler = move || {
println!("signal handler called!");
let appu = weak.upgrade();
match appu {
Some(uapp) => uapp.borrow().work(),
None => panic!("user error, app no longer alive")
}
};
install(libc::SIGALRM, Arc::from(signalhandler));
app
}
}
fn main() {
// install a signal handler for SIGALRM
unsafe {
let mut action: libc::sigaction = mem::zeroed();
action.sa_sigaction = handler as usize;
action.sa_flags = libc::SA_RESTART;
if libc::sigaction(libc::SIGALRM, &action, std::ptr::null_mut()) != 0 {
panic!("cannot set signal handler");
}
}
let _app = Application::new("my application");
println!("Arming timer...");
unsafe {
libc::alarm(1);
libc::sleep(2);
}
}