Is boost::interprocess::shared_ptr threadsafe (and

2019-02-17 13:12发布

问题:

I want to share data between threads, and have it automatically deleted when the last user is done with it. This seems to work, most of the time, using boost::interprocess::shared_ptr in a boost::fixed_managed_shared_memory segment: but not always.

So, is boost::interprocess::shared_ptr thread (and interprocess) -safe?

If I'm using my shared memory at a fixed address (I'm pretty certain this is going to be okay in my 64-bit (well, 48-bit) address space), is it possible to use a normal boost::shared_ptr (which are threadsafe) instead?

some clarification:

The pointer type I use is plain void*, (my shared memory is mapped to a fixed address).

The question of threadsafety is about the reference count -- i.e., whether copying/destroying shared pointers to the same thing in different processes at the same time is permitted. Not access to the same shared pointer in different threads, and not access to the pointee.

回答1:

The reference count used in boost::interprocess:shared_ptr is implemented using an atomic counter defined in boost/interprocess/detail/atomic.hpp with the refcount logic mainly implemented by boost/interprocess/smart_ptr/detail/sp_counted_base_atomic.hpp. The intent is to have the refcount be handled in a thread (and interprocess) safe manner.

The atomic operation implementations differ depending on the specific target platform (Windows uses the Win32 Interlocked APIs, some platforms use various inline assembly, etc). It might be helpful to know what platform you're targeting. I suppose that you may be running into a bug in the refcount handling, though I wouldn't count on it.

I've restricted the above answer to the area you wanted specifically addressed:

The question of threadsafety is about the reference count -- i.e., whether copying/destroying shared pointers to the same thing in different processes at the same time is permitted. Not access to the same shared pointer in different threads, and not access to the pointee.

That said, I'd look at bugs introduced possibly by the items you mention above or by somehow creating 'independent' boost::interprocess:shared_ptr objects (where different shared_ptrs refer to the same object using different refcounts). This situation can be easy to have happen if you have some code that continues to use and/or pass around the raw object pointer.



回答2:

boost::shared_ptr<T> is not interprocess safe, so whether it is multithread safe in this context is moot. (This statement assumes that BOOST_SP_DISABLE_THREADS has not been #defined for the program's operation.)

boost::interprocess::shared_ptr<T> is, in its very nature, designed to be cross-process safe, as well as multithread safe in its nature. When the last reference goes out of scope, the pointed-at object can be cleaned up. Obviously, this cleaning up happens within the bounds of the shared memory segment used for the object.

Since boost::shared_ptr<T> uses a lock-free counting mechanism at version 1.33.0 on many platforms, it is unlikely except by the remotest of chances that cross-process deletion of an object in a shared_memory segment would succeed, and does not appear to be supported functionality by the Boost maintainers.



回答3:

Er. boost::shared_ptr is most definitely not thread-safe. At least not more thread-safe than e.g. std::vector. You may read a boost::shared_ptr from multiple threads, but as soon as any thread is writing a boost::shared_ptr it must synchronize with other writers and readers.

And no, you can not use it in shared memory, it was never designed to be. E.g. it uses a so called "shared count" object that stores the reference-count and the deleter, and that shared-count object is allocated by the shared_ptr code, so it will not reside in shared memory. Also the shared_ptr code (and meta data like vtables) might be at totally different addresses in different processes, so any virtual function call would also be a problem (and IIRC shared_ptr uses virtual functions internally - or at least function pointers, which leads to the same problem).


I don't know if boost::interprocess::shared_ptr is interprocess-safe, but I'm pretty sure it's not. Interprocess synchronization is pretty expensive. Having boost::interprocess::shared_ptr not do it makes it possible for the user to block accesses to shared data. That way the high synchronization cost only has to be paid once for multiple accesses in a row.

EDIT: I would expect that the usage pattern that Eamon Nerbonne refered to in his comment (which is thread-safe with boost::shared_ptr), is also OK with boost::interprocess::shared_ptr. Can't say for sure though.



回答4:

"This seems to work, most of the time, using boost::interprocess::shared_ptr in a boost::fixed_managed_shared_memory segment: but not always." If not always means that deletion don't work always: Just use a semaphore with your thread safe container. This semaphore is not improve thread safety, but you can verify and even limit how many user use the data. If semaphore is 0, then no more user, safe delete the shared data. If only one user there, this will be 1, so copy out the user-requested data, delete the shared container, then return with the copy.



回答5:

Looking through the code in shared_ptr.hpp, and at the docs on the boost website, it would seem as though dereferencing a single instance may or may not be threadsafe depending on the second template parameter, which determines the internal pointer type to be used. Specifically, "The internal pointer will be of the same pointer type as typename VoidAllocator::pointer type (that is, if typename VoidAllocator::pointer is offset_ptr, the internal pointer will be offset_ptr)." And since dereferences merely return the result of get()/get_pointer() method of this class, it should probably depend entirely on that. Boost::shared_ptr will work if you want simultaneous read-only access. For write access from multiple threads, you might have to write your own wrapper modelled after offset_ptr.



回答6:

As pgroke alludes to (not sure why the downvotes) the core question is whether you are accessing the same shared_ptr instance from different threads or processes.

shared_ptr (interprocess or not) does not support this scenario, and this will not be safe.

On the other hand, shared_ptr is designed to have multiple (thread-private, or protected from concurrent modification via some other mechanism) shared pointer instances point to the same object, and have different instances of these pointers to the same object be modified concurrently without issue.

::interprocess:: here is mostly a red-herring - it doesn't change the thread-safety of the pointer, just makes sure there are no internal pointers that refer to process-private memory, etc.

So which of the two cases is it?