We have a program where multiple threads call RefCell.borrow() concurrently on the same RefCell object.
There are NO RefCell.borrow_mut() anywhere in this program on this object.
When we run, we get error:
"already mutably borrowed: BorrowError", even though there's no borrow_mut() anywhere in our program.
We suspect it is because RefCell.borrow() is not thread-safe with itself and the error message is due to some "unobservable internal state" of RefCell when RefCell.borrow() is concurrently called.
EDIT:
The RefCell is a member of another Struct Foo. We mark unsafe impl Send for Foo {}.
RefCell is !Sync so it is never valid to have references to the same RefCell on multiple threads. (In safe Rust, the compiler will reject code that shares such references across threads.) For more details, see the std::cell docs.
RefCell::borow reads and writes to the RefCell’s internal counters without using atomic operations or locks, so it is definitely not thread-safe. For values shared across threads, you should use something like RwLock instead of RefCell.
Why are you using RefCell for a value that you never borrow mutably? The only benefit of RefCell is to safely mutate a shared value. If you don’t need that, then share the value directly without wrapping it in a cell or lock.
It is "thread safe" only in the sense that it being !Sync is supposed to statically prevent it from being concurrently accessed from different threads. It is not equipped to handle being concurrently accessed from different threads.
If you truly are concurrently calling RefCell::borrow on the same ref cell from multiple threads, then you must be using unsafe code incorrectly somehow. Since RefCell is !Sync, any way of using unsafe code that results in multiple threads having &RefCell to the same RefCell at the same time is by definition wrong (and will not work in practice either, since RefCell is not equipped to handle being concurrently accessed from different threads).
If you are not using any unsafe code, then perhaps you are mistaken about your claim that multiple threads have references to the same RefCell at the same time.
The closest thread-safe equivalents to RefCell are Mutex and RwLock. However, the fact that you are never calling RefCell::borrow_mut is strange. If you have no need to ever mutably access the underlying type, then it is not necessary to put it in any wrapper, you can just share an immutable reference to it directly.
Send alone shouldn’t cause this problem (though it could be incorrect in other ways). Do you also impl Sync for Foo? Or does Foo contain some sort of shared pointer like Rc<RefCell<T>> or &RefCell<T> or Arc<RefCell<T>>?
Implementing Sync and then allowing concurrent access to a RefCell is definitely incorrect and will lead to undefined behavior.