Description
There is an unsafe impl<T> Sync for KeyInner<T> { }
in the scoped TLS module, which allows for sharing of data that wasn't meant to be shared.
An unqualified unsafe impl<T> Sync for Foo { }
should only be used when Foo
provides some sort of threadsafe locking mechanism. KeyInner
and ScopedKey
do not.
This lets us, for example, clone an Rc
or RefCell
between threads, in safe code:
scoped_thread_local!(static RC: Rc<u8>);
fn main() {
RC.set(&Rc::new(1), ||{
let y = &RC;
let guard = scoped(|| {
println!("Started 1");
y.with(|slot|{
for i in 1..10 {
println!("Cloned in 1");
slot.clone();
sleep_ms(10);
}
});
println!("Ended 1");
});
println!("Started 0");
y.with(|slot| {
for i in 1..10 {
println!("Cloned in 0");
slot.clone();
sleep_ms(10);
}
});
println!("Ended 0");
guard.join();
});
}
Here, we have Rc
clones from different threads being interleaved, which can cause unsafety if there's a race on the refcount. We could also cause unsafety with a RefCell
, or any other non-Sync type. ScopedKey
basically lets us share &T
across threads even if T: !Sync
. I put the sleeps in there because somehow without them the routines are run serially.
Note that this is not a problem with the scoped()
API. I'm using scoped()
here because to exploit &T: Send, T:!Sync
one needs to be able to share across threads, and Arc
has an additional Send
bound which ScopedKey
doesn't satisfy. Any usable scoped
API will probably have to make the assumption that &T: Send iff T:Sync
. Nor is this a problem with Rc
, since it can be done with RefCell
, too.
Solution: Make it unsafe impl<T: Sync> Sync for KeyInner<T> { }
... I think. I'm not familiar with this API, and I'm not sure why it's even Sync
in the first place.