Home > Back-end >  How to store a thread join handle inside Rust struct
How to store a thread join handle inside Rust struct

Time:12-04

I have a requirement to create a network receiver thread by storing the thread join handle inside a network receiver struct. But I'm getting errors error[E0506]: cannot assign to self.join_handle because it is borrowed and error[E0521]: borrowed data escapes outside of associated function, while I'm creating the thread inside the start method. This is my code

use std::thread;
use std::sync::atomic::{AtomicBool, Ordering};

pub struct NetworkReceiver {
    terminate_flag: AtomicBool,
    join_handle: Option<thread::JoinHandle<()>>
}

impl NetworkReceiver {

    pub fn new() -> NetworkReceiver {
        let net_recv_intf = NetworkReceiver {
            terminate_flag: AtomicBool::new(false),
            join_handle: None
        };

        net_recv_intf
    }

    pub fn start(&mut self) {
        self.join_handle = Some(thread::spawn(|| self.run()));
    }

    pub fn run(&mut self) {
        while !self.terminate_flag.load(Ordering::Relaxed) {
            
        }
    }

    pub fn terminate(&mut self) {
        self.terminate_flag.store(true, Ordering::Relaxed);
    }
}

CodePudding user response:

You could separate the join_handle from everything else inside NetworkReceiver like this:

use std::thread;
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
};

pub struct NetworkReceiver {
    terminate_flag: AtomicBool,
}
pub struct RunningNetworkReceiver {
    join_handle: thread::JoinHandle<()>,
    network_receiver: Arc<NetworkReceiver>,
}

impl NetworkReceiver {
    pub fn new() -> NetworkReceiver {
        let net_recv_intf = NetworkReceiver {
            terminate_flag: AtomicBool::new(false),
        };

        net_recv_intf
    }

    pub fn start(self) -> RunningNetworkReceiver {
        let network_receiver = Arc::new(self);
        let join_handle = {
            let network_receiver = network_receiver.clone();
            thread::spawn(move || network_receiver.run())
        };
        RunningNetworkReceiver {
            join_handle,
            network_receiver,
        }
    }
    pub fn run(&self) {
        while !self.terminate_flag.load(Ordering::Relaxed) {
        }
    }
}
impl RunningNetworkReceiver {
    pub fn terminate(&self) {
        self.network_receiver.terminate_flag.store(true, Ordering::Relaxed);
    }
}

If you need exclusive acces to anything inside of NetworkReceiver after all you'd have to wrap it (or the part that needs it) in a RwLock or Mutex or similar.

CodePudding user response:

When using variables in a thread, that thread must have ownership. This line (thread::spawn(|| self.run()); tries to move self into the thread but cannot because self needs to outlive the function.

I beleive you'll have to wrap your NetworkReceiver within an Arc Mutex. You could change your NetworkReceiver::new() to return a Arc<Mutex> and all the associated functions would change from fn foo(&mut self) to fn foo(self: Arc<Self>)

CodePudding user response:

use std::thread;
use std::sync::atomic::{AtomicBool, Ordering};

pub struct NetworkReceiver {
    terminate_flag: AtomicBool,
    join_handle: Option<thread::JoinHandle<()>>
}

impl NetworkReceiver {
    pub fn new() -> NetworkReceiver {
        let net_recv_intf = NetworkReceiver {
            terminate_flag: AtomicBool::new(false),
            join_handle: None
        };

        net_recv_intf
    }

    pub fn start(&mut self) {
        let join_handle = thread::spawn(|| self.run());
        self.join_handle = Some(join_handle);
    }

    fn run(&self) {
        let mut buff: [u8; 2048] = [0; 2048];

        while !self.terminate_flag.load(Ordering::Relaxed) {
            // Do something here
        }
    }

    pub fn terminate(&mut self) {
        self.terminate_flag.store(true, Ordering::Relaxed);
    }
}

fn main() {
    let mut net_recv = NetworkReceiver::new();
    net_recv.start();
    net_recv.terminate();
}

I've made a few changes to the code you provided:

I've added a main function that creates an instance of NetworkReceiver and calls the start() and terminate() methods on it. I've made the run() method a private method because it should only be called by the start() method. I've made the run() method take a reference to self instead of a mutable reference, because it doesn't modify the NetworkReceiver instance.

  • Related