From articles like std::shared_ptr thread safety, I know that the control block of a std::shared_ptr is guaranteed to be thread-safe by the standard whilst the actual data pointed to is not inherently thread-safe (i.e., it is up to me as the user to make it so).
What I haven't been able to find in my research is an answer to how this guaranteed. What I mean is, what mechanism specifically is used to ensure that the control block is thread safe (and thus an object is only deleted once)?
I ask because I am using the newlib-nano C library for embedded systems along with FreeRTOS. These two are not inherently designed to work with each other. Since I never wrote any code to ensure that the control block is thread safe (e.g., no code for a critical section or mutex), I can only assume that it may not actually be FreeRTOS thread-safe.
CodePudding user response:
There isn't really much machinery required for this. For a rough sketch (not including all the requirements/features of the standard std::shared_ptr
):
You only need to make sure that the reference counter is atomic, that it is incremented/decremented atomically and accessed with acquire/release semantics (actually some of the accesses can even be relaxed).
Then when the last instance of a shared pointer for a given control block is destroyed and it decremented the reference count to zero (this needs to be checked atomically with the decrement using e.g. std::atomic::fetch_add
's return value), the destructor knows that there is no other thread holding a reference to the control block anymore and it can simply destroy the managed object and clean up the control block.
CodePudding user response:
MSVC uses InterlockedIncrement to increment the refcount, which is an atomic increment.
CodePudding user response:
My best guess from looking at the C library code is that the reference count is implemented as an atomic operation. I am thinking that all the code eventually boils down to a set of built-in function implemented by the compiler for the specific architecture I am using (ARM). Here a list of those built-ins provided by GCC: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html