Is generator thread safe?

Just curious if I can resume a generator from multi threads. If so how to pass generator to multi threads?

This depends on whether the generator's internal state is thread-safe. For example, this generator which captures an Arc can be sent to another thread:

let mut arc = Arc::new(true);
let mut generator0 = move || {
    yield arc.clone()
};
spawn(move || Pin::new(&mut generator0).resume(()));

But if we change the Arc to Rc:

let mut rc = Rc::new(true);
let mut generator1 = move || {
    yield rc.clone()
};
spawn(move || Pin::new(&mut generator1).resume(()));

then we get this compiler error:

error[E0277]: `Rc<bool>` cannot be sent between threads safely
   --> src/main.rs:24:11
    |
24  |     spawn(move || Pin::new(&mut generator1).resume(()));
    |     ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc<bool>` cannot be sent between threads safely
    |     |
    |     required by a bound introduced by this call
    |
    = help: within `GeneratorState<Rc<bool>, ()>`, the trait `Send` is not implemented for `Rc<bool>`
    = note: required because it appears within the type `GeneratorState<Rc<bool>, ()>`
note: required by a bound in `spawn`

(Playground)

Following code is my try , the program panic at line A because of multi references on the Arc. So what is the right way?

fn generator() {
  let mut x = || {
    let mut sum = 0;
    for i in 1..=10 {
      sum+=i;
      yield i;
    }
    sum
  };
  
  let mut arc = Arc::new(x);  
  let mut t = vec![];
  for i in 1..=3 {
    let mut x = arc.clone();
    let y = move|| {
      let mut x = Arc::get_mut(&mut x).unwrap();  // A
      loop {
        match Pin::new(&mut x).resume(()) {
          GeneratorState::Yielded(x) => println!("yield {}",x),
          GeneratorState::Complete(x) => {
            println!("complete {}",x);
            break;
          }
        }
      }
    };
    t.push(thread::spawn(y));
  }
  for i in t {
    i.join();
  }
}

Arc::get_mut is rarely useful; it can only be used while the Arc has no clones.

If you want to mutate a value while it is shared across threads, you'll need to protect it with some sort of synchronization, like a Mutex:

let arc = Arc::new(Mutex::new(x));  

and then .lock() the mutex when you need to mutate its contents:

match Pin::new(&mut *x.lock().unwrap()).resume(()) {

See this playground for a complete example.

3 Likes

thank you. got it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.