Skip to content

Commit

Permalink
Use Acquire load only before dropping
Browse files Browse the repository at this point in the history
  • Loading branch information
mgeier committed Apr 18, 2022
1 parent 0354f9d commit 32ccd67
Showing 1 changed file with 13 additions and 5 deletions.
18 changes: 13 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,15 +250,23 @@ impl<T> RingBuffer<T> {
unsafe fn abandon<T>(buffer: NonNull<RingBuffer<T>>) {
// The "store" part of `fetch_or()` has to use `Release` to make sure that any previous writes
// to the ring buffer happen before it (in the thread that abandons first).
// The "load" part has to use `Acquire` to make sure that reading `head` and `tail`
// in the destructor happens after it (in the thread that drops the `RingBuffer`).
if buffer
.as_ref()
.is_abandoned
.fetch_or(true, Ordering::AcqRel)
.fetch_or(true, Ordering::Release)
{
// The flag was already set, i.e. the other thread has already abandoned
// the RingBuffer and it can be dropped now.
// The flag was already set, i.e. the other thread has already abandoned the RingBuffer
// and it can be dropped now.

// However, since the load of `is_abandoned` was `Relaxed`,
// we have to use `Acquire` here to make sure that reading `head` and `tail`
// in the destructor happens after this point.

// Ideally, we would use a memory fence like this:
//core::sync::atomic::fence(Ordering::Acquire);
// ... but as long as ThreadSanitizer doesn't support fences,
// we use load(Acquire) as a work-around to avoid false positives:
let _ = buffer.as_ref().is_abandoned.load(Ordering::Acquire);
drop_slow(buffer);
} else {
// The flag wasn't set before, so we are the first to abandon the RingBuffer
Expand Down

0 comments on commit 32ccd67

Please sign in to comment.