AtomicRefCell Send requirement

Hi, I’m reading the source of the atomic_refcell crate and I don’t understand why AtomicRefCell<T> requires T to be Sync in order to be Send.

AtomicRefCell will hand out multiple shared references to the inner value at the same time. If the inner type is not Sync (i.e. can be mutated in a non-threadsafe manner through a shared reference), then the AtomicRefCell can’t be Send or multiple threads would be able to have shared references to the value concurrently.

Yes but the AtomicRefCell is borrowed, it can’t be moved to another thread while there is a Ref in the wild.
It’s the same case as RwLock and it only requires Send (either std or parking_lot’s version).

This looks like an overly conservative bound to me, Send should only require T: Send but not T: Sync. Same as RwLock

2 Likes

Adding a T : Sync bound on AtomicRefCell<T> : Send expresses the following:

  • If T : !Sync (i.e., T may not be sound to share / be aliased across multiple threads, such as T = Cell<_> or T = RefCell<_>) then it may be unsound to send an unaliased AtomicRefCell<T> into another thread.

The only wrappers that need to worry about it are those offering shared (aliased) ownership, such as Rc and Arc, since a unique handle on a Arc does not guarantee that the pointee is not aliased.

But AtomicRefCell<T> does not offer shared ownership: if you have a unique handle on a AtomicRefCell<T>, you can obtain a unique handle on the wrappee T.

Thus the Send impl should be:

unsafe impl<T : ?Sized> Send
    for AtomicRefCell<T>
where
    T : Send,
{}

(Regarding the Sync impl, since &AtomicRefCell<T> -> &T is possible, it requires at least T : Sync, and since &AtomicRefCell<T> -> &mut T is possible, it also requires T : Send)

4 Likes

Alright, I’m glad I didn’t overlook some edge case.

Since I can’t choose your two replies as the solution I’ll pick @Yandros’ for posterity and the hard work put into it. But @KrishnaSannasi, yours was enough for me =)
Thank you both.

2 Likes