This question is specifically about trivially destructible types within reference counted pointers. See the example from Boost's documentation on uses of atomics.
The decrement is as follows:
if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) {
// A
boost::atomic_thread_fence(boost::memory_order_acquire);
delete x;
}
We know that, due to memory_order_release
, all reads/writes of x
are completed before the fetch_sub
(see here). Thus, if we happen to reach point A
then all uses of x
are complete.
At point A
in the code, we are not guaranteed by the standard to see the latest value of x
until after the memory_order_acquire
fence...
So here is my question regarding the second statement about memory_order_acquire
:
When x
points to a trivially destructible type (for example, int
where x
is int * const
) is the memory_order_acquire
pointless? The rationale I have is because if x
is trivially destructible then the latest changes to x
does no affect the deletion of x
?
For example, whether the deleting thread's delete x;
sees the latest x
such that *x = 10
or an outdated value such that *x = 8
the destruction process is always the same regardless (as long as the pointer x
itself remains constant). It knows that no one is going to modify the x
from that point thanks to the release and so all it has to do is deallocate.
Is there another benefit of memory_order_acquire
that I am missing here?
Is my thinking correct and if not then why do we need to see the latest value of x
on the deleting thread?
No way!
You seem to believe that barriers are a tool to be able to
But that's just one example of the use of atomics and barriers.
In general barriers associated with relaxed atomic operation make mutual exclusion well defined. A mutex is a mutual exclusion device, a null then non null atomic pointer is another, and a reference count is yet another.
A reference count functions like a RW lock, with:
The (RC=0) observation is the analog of lock operation because it must have mutual exclusion with the property (RC>0). Mutual exclusion translates to a release-acquire pair, for each series of computations that need exclusion. All the users of the data controlled by the RC device need mutual exclusion with the memory release (not mutex release) operation.