1

I'm trying to write a simple Observer-pattern-ish thing with events and callbacks. The idea is that when the thing owning the mutating function dies (struct Obj {cb: Arc<Mutex<FnMut>>, .. }), it drops the Arc. After that, any Weak references will be (effectively) dead and can be detected on access. I was ALSO hoping that this would decouple the FnMut from Thread's 'static lifetime. I might need to wrap the whole thing in another Arc<Rwlock<T>> to prevent the Weak references from doing thread unsafe stuff, but that's a different problem.

The closest solution I've gotten was this: How can I send a function to another thread?

However, I seem to have the Arc<Mutex<T>> and adding lifetimes (I may have done that wrong, though) didn't seem to help. I'm kind of lost as to what is actually wrong.

I wrote a minimal example:

use std::{
    collections::HashMap,
    sync::{
        mpsc::{self, Receiver},
        Arc, Mutex, Weak,
    },
    thread::{self, JoinHandle},
};

struct Callback {
    cb_w: Weak<Mutex<FnMut()>>,
}

type FnMapping = Arc<HashMap<String, Callback>>;

fn start_thread<'a>(rx: Receiver<String>, mapping: FnMapping) -> JoinHandle<()> {
    thread::spawn(move || {
        match rx.recv() {
            Ok(v) => {
                if let Some(o) = mapping.get(&v) {
                    match o.cb_w.upgrade() {
                        Some(cb_m) => match cb_m.lock() {
                            Ok(cb_lg) => (*cb_lg)(),
                            Err(e) => (),
                        },
                        None => { /* TODO owner is gone, mark for delete */ }
                    }
                }
            }
            Err(e) => (),
        }
    })
}

fn main() {
    let mapping: FnMapping = Arc::new(HashMap::new());
    let (tx, rx) = mpsc::channel();
    drop(tx);
    start_thread(rx, mapping)
        .join()
        .expect("Could not join thread -- failed to terminate?");
    println!("Leaving the test bay.");
}

This fails to compile with the following error:

error[E0277]: `(dyn std::ops::FnMut() + 'static)` cannot be sent between threads safely
  --> src/main.rs:17:5
   |
17 |     thread::spawn(move || {
   |     ^^^^^^^^^^^^^ `(dyn std::ops::FnMut() + 'static)` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `(dyn std::ops::FnMut() + 'static)`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<(dyn std::ops::FnMut() + 'static)>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Weak<std::sync::Mutex<(dyn std::ops::FnMut() + 'static)>>`
   = note: required because it appears within the type `Callback`
   = note: required because it appears within the type `(std::string::String, Callback)`
   = note: required because it appears within the type `std::marker::PhantomData<(std::string::String, Callback)>`
   = note: required because it appears within the type `std::collections::hash::table::RawTable<std::string::String, Callback>`
   = note: required because it appears within the type `std::collections::HashMap<std::string::String, Callback>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::collections::HashMap<std::string::String, Callback>>`
   = note: required because it appears within the type `[closure@src/main.rs:17:19: 32:6 rx:std::sync::mpsc::Receiver<std::string::String>, mapping:std::sync::Arc<std::collections::HashMap<std::string::String, Callback>>]`
   = note: required by `std::thread::spawn`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
AlterionX
  • 81
  • 1
  • 10

0 Answers0