Home > Back-end >  How to pass a function as parameter for thread?
How to pass a function as parameter for thread?

Time:11-26

I'm a new rustacean and I try to pass a function as argument in another function in order to create threads with the function pass as argument.

Here the code:

use std::os::unix::net::{UnixListener, UnixStream};
use std::thread;
use std::io::Read;
use anyhow::Context;

pub struct SocketServer {
    path: String,
    listener: UnixListener,
}

impl SocketServer {
    pub fn new(path: &str) -> anyhow::Result<SocketServer>{

        if std::fs::metadata(path).is_ok() {
            println!("A socket is already present. Deleting...");
            std::fs::remove_file(path).with_context(|| {
                format!("could not delete previous socket at {:?}", path)
            })?;
        }

        let socket_listener =
            UnixListener::bind(path).context("Could not create the unix socket")?;

        let path = path.to_string();

        Ok(SocketServer{ path, listener: socket_listener })
    }

    pub fn start(&self, f: &dyn Fn(UnixStream)) -> anyhow::Result<()>{

        for stream in self.listener.incoming() {
            match stream {
                Ok(stream) => {
                    thread::spawn(||f(stream));
                }
                Err(err) => {break;}
            }
        }
        Ok(())
    }
}

pub fn handle_stream(mut unix_stream: UnixStream) -> anyhow::Result<()> {
    let mut message = String::new();
    unix_stream
        .read_to_string(&mut message)
        .context("Failed at reading the unix stream")?;

    println!("We received this message: {}", message);

    Ok(())
}

Here the error I get when in try to compile:

error[E0277]: `dyn Fn(UnixStream)` cannot be shared between threads safely
   --> src/socket.rs:34:35
    |
34  |                     thread::spawn(||f(stream));
    |                     ------------- ^^^^^^^^^^^ `dyn Fn(UnixStream)` cannot be shared between threads safely
    |                     |
    |                     required by a bound introduced by this call
    |
    = help: the trait `Sync` is not implemented for `dyn Fn(UnixStream)`
    = note: required for `&dyn Fn(UnixStream)` to implement `Send`

I got some information in the Rust book but I still don't understand which function need to implement what. Can you give me some hints? (Advice on other parts are welcome too)

I tried to remove closure but it goes to another error:

error[E0277]: expected a `FnOnce<()>` closure, found `()`
   --> src/socket.rs:34:35
    |
34  |                     thread::spawn(f(stream));
    |                     ------------- ^^^^^^^^^ expected an `FnOnce<()>` closure, found `()`
    |                     |
    |                     required by a bound introduced by this call
    |
    = help: the trait `FnOnce<()>` is not implemented for `()`
    = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `spawn`
   --> /rust/lib/rustlib/src/rust/library/std/src/thread/mod.rs:661:8
    |
661 |     F: FnOnce() -> T,
    |        ^^^^^^^^^^^^^ required by this bound in `spawn`

CodePudding user response:

Is there a reason you need a dyn Fn? The easiest is probably to accept a impl Fn(UnixStream) Send Clone 'static instead

pub fn start<F>(&self, f: F) -> anyhow::Result<()>
where
    F: Fn(UnixStream)   Send   Clone   'static,
{
    for stream in self.listener.incoming() {
        let f = f.clone();
        match stream {
            Ok(stream) => {
                thread::spawn(move || f(stream));
            }
            Err(err) => break,
        }
    }
    Ok(())
}

CodePudding user response:

The dyn Fn() may capture things (such as Rcs) that are unsafe to share between threads. So you need to mark it Sync:

pub fn start(&self, f: &(dyn Fn(UnixStream)   Sync)) -> anyhow::Result<()> {

Now you'll get another error:

error[E0521]: borrowed data escapes outside of associated function
  --> src/lib.rs:34:21
   |
30 |     pub fn start(&self, f: &(dyn Fn(UnixStream)   Sync)) -> anyhow::Result<()> {
   |                         -  - let's call the lifetime of this reference `'1`
   |                         |
   |                         `f` is a reference that is only valid in the associated function body
...
34 |                     thread::spawn(|| f(stream));
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                     |
   |                     `f` escapes the associated function body here
   |                     argument requires that `'1` must outlive `'static`

Because the function's captured data may be dropped before the thread exits, causing use after free. The easiest solution is to use Arc:

pub fn start(&self, f: Arc<dyn Fn(UnixStream)   Sync   Send>) -> anyhow::Result<()> {
    for stream in self.listener.incoming() {
        match stream {
            Ok(stream) => {
                let f = Arc::clone(&f);
                thread::spawn(move || f(stream));
            }
            Err(err) => {
                break;
            }
        }
    }
    Ok(())
}

(You also need a Send bound).

  • Related