Hey,
I was trying to improve my project if have managed to write the following piece of code that will happily compile, even though it shouldn't:
{
let job_sys = JobSystem::new(2, 128).unwrap();
let js = JobScope::new_from_system(&job_sys);
let mut v: u32 = 0;
for _ in 0..100 {
let x = &mut v;
let handle = js.create(move || {*x += 10}).unwrap(); // <-- problem here
js.run(&handle).unwrap();
}
}
In the above snippet it is possible to create multiple closures with a mutable reference to v that will be run on worker threads once we reach js.run(..). At run time this would be a race condition. I've also tried replacing v with a Vec and I get the same result.
The handle is defined as:
pub struct ScopedJobHandle<'scope, T> {
h: usize,
p: PhantomData<T>
s: PhantomData<&'scope mut ()>,
}
I tried adding a PhantomData<T> in an attempt to circumvent the above problem, but it has no effect on the above code.
and the function signature for create is
pub fn create<T>(&self, job: T) -> Result<ScopedJobHandle<T>, Error>
where
T: Sized + FnOnce() + Send
Another thing I noticed is, if i try to store the handles in a Vec, it does indeed complain about this:
let mut jobs = vec![];
{
let job_sys = JobSystem::new(2, 128).unwrap();
let js = JobScope::new_from_system(&job_sys);
let mut v: u32 = 0;
for _ in 0..100 {
let x = &mut v;
let handle = js.create(move || {*x += 10}).unwrap(); // <-- problem here
js.run(&handle).unwrap();
jobs.push(handle); // <-- get error here about multiple mutable borrows of v
}
}
Error:
|
611 | let x = &mut v;
| ^^^^^^ `v` was mutably borrowed here in the previous iteration of the loop
This in turn leads me to believe that PhantomMarker<T> on ScopedJobHandle will work if keep the handles alive during the execution of the job. That being said, I would also like to use this in a fire and forget sort of approach.
Am I missing something here? Shouldn't the trait requirement of Send prevent this in the first place?