Threading in vector for whole program

I have a struct that contains a vector of jobs that are to be spawned in their own thread.

ie

pub struct Job {
    run: Box<FnMut() + Send + Sync>,
}

pub struct JobScheduler {
    jobs: Arc<Vec<Mutex<Job>>>,
    guards: Vec<thread::JoinHandle<()>>
}

I also have a method of that struct that should run these jobs ie something like

pub fn tick_async(&mut self) {
        for job in &mut self.infinite_jobs.clone().iter() {
                let guard = thread::spawn(move || {
                    let j = job.lock();
                    if j.is_ok() {
                        j.unwrap().do_long_running_op();
                    }
                });

                self.guards.push(guard);
}

I can't run this as the threads may outlast the JobScheduler object.
I want JobScheduler to last as long as the whole program so how can I mark it so ?
I was hoping to clean up in a deconstructor function using the saved JoinHandle's .

Note, this is me playing around. I know I can use crossbeam to make sure the method outlasts the threads but I want to keep a list of all threads and destroy them at program end if possible.

Thanks

One option is to use Box::leak to get an &'static mut JobScheduler:

let scheduler = Box::leak(Box::new(JobScheduler::new()));

However, in your example, it looks like the threads spawned by tick_async don't capture the JobServer (self) but rather the job variable which is borrowed from a temporary clone of self.infinite_jobs, which I'm guessing is another Arc<Vec<Mutex<Job>>> (in which case job has type &Mutex<Job>).

Can you change this to a Vec<Arc<Mutex<Job>>> instead, so that each thread can capture an owning Arc<Mutex<Job>>, instead of a borrowed reference?

pub struct JobScheduler {
    infinite_jobs: Vec<Arc<Mutex<Job>>>,
    guards: Vec<thread::JoinHandle<()>>
}

impl JobScheduler {
    pub fn tick_async(&mut self) {
        for job in self.infinite_jobs.iter() {
            let job = job.clone();
            let guard = thread::spawn(move || {
                let j = job.lock();
                if j.is_ok() {
                    j.unwrap().do_long_running_op();
                }
            });
            self.guards.push(guard);
        }
    }
}

If I understand you correctly something like

I couldn't get around the lifetime issues.

It works if you add the job.clone() like in my example above:

1 Like

That works great. Thanks

1 Like