Sadly sendable Generator is currently unsound because thread local variables may leak non-sendable values from within the our coroutine. Here's example:
#![forbid(unsafe_code)]
use std::{cell::Cell, rc::Rc, thread};
use generator::Gn;
const COUNT_ITERATIONS: u64 = 1000000000;
fn main() {
thread_local! {
static COUNTER: Cell<Option<Rc<Cell<u64>>>> = Cell::new(None);
}
let counter = Rc::new(Cell::new(0));
COUNTER.with(|tl| tl.set(Some(Rc::clone(&counter))));
let mut generator = Gn::new_scoped(move |mut s| {
let counter = COUNTER.with(|tl| tl.take()).unwrap();
s.yield_with(());
println!("Rc {:p} in thread {:?}", counter, thread::current());
for _ in 0..COUNT_ITERATIONS {
counter.set(counter.get() + 1)
}
});
generator.next().unwrap();
let child = thread::spawn(move || {
generator.next().unwrap();
});
println!("Rc {:p} in thread {:?}", counter, thread::current());
for _ in 0..COUNT_ITERATIONS {
counter.set(counter.get() + 1)
}
child.join().unwrap();
assert_eq!(counter.get(), 2 * COUNT_ITERATIONS);
}
This is a long known issue. This is why may spawn is unsafe. You should use coroutine_local! here. And I have no better idea to design the API, do you have any suggestions?
Sadly sendable
Generator
is currently unsound because thread local variables may leak non-sendable values from within the our coroutine. Here's example:Output:
See https://blaz.is/blog/post/lets-pretend-that-task-equals-thread/ and https://users.rust-lang.org/t/controversial-opinion-keeping-rc-across-await-should-not-make-future-send-by-itself/100554/12 for details.