7

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.

James
  • 24,676
  • 13
  • 84
  • 130
  • 1
    I don't know the detail but it would seem rather contradictory if boost::interprocess::shared_ptr were not interprocess safe. Why would they have it there? – CashCow Mar 20 '11 at 01:51
  • 1
    Because it can be placed *in* shared memory, whereas the `boost::shared_ptr` can't (since it uses virtual inheritance, amongst other things). – James Mar 20 '11 at 02:10
  • 1
    still, that would mean you'd need to manually syncronize the end-of-life of that `shared_ptr`? That totally violates RAII, so they certainly *should* be just as thread safe as the usual shared_ptr. – Eamon Nerbonne Mar 27 '11 at 04:20
  • Yeah, I expect it to be. I really would like someone to confirm that it is safe (preferably with some evidence), so I can blame my crashiness on my code... – James Apr 25 '11 at 17:03
  • See my comment below - you need to clarify whether you are accessing a particular shared_ptr instance (talking about the pointer itself, not the pointed-to object) from multiple threads or processes. This is not safe. – BeeOnRope Apr 26 '11 at 00:13
  • See above: I am copying/destroying **different** shared pointer instances in difference processes. – James Apr 26 '11 at 10:20

6 Answers6

5

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.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
1

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.

Andy Finkenstadt
  • 3,547
  • 1
  • 21
  • 25
  • Why would lock free by itself mean not interprocess safe? – SoapBox Apr 25 '11 at 10:31
  • Not all platforms use the lock-free method, and the maintainers of `boost` do not guarantee that `boost::shared_ptr` will continue to use a lock-free mechanism going forward. Most importantly, the cross-process guarantee is **not present** in the contract for `boost::shared_ptr` as it is for `boost::interprocess::shared_ptr`. – Andy Finkenstadt Apr 27 '11 at 22:42
0

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.

Paul Groke
  • 6,259
  • 2
  • 31
  • 32
  • [boost::shared_ptr is threadsafe](http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/shared_ptr.htm#ThreadSafety) in the sense that multiple *distinct* instances that happen to refer to the same shared pointer can be concurrently read and modified; that's enough to be useful. – Eamon Nerbonne Mar 27 '11 at 04:04
  • You mean multiple shared pointers that point to the same pointee (not the same shared pointer). I'm aware of that. And of course it's enough to be useful. It's not enough however to claim that shared_ptr "is thread-safe" (without further explanation), because, as I wrote, some usage scenarios are not. And I believe that the OP isn't aware of that. – Paul Groke Mar 27 '11 at 04:18
  • Then you should really update this answer; because that's a rather important level of threadsafety - a guarantee `std::vector` has no equivalent for, for instance (so it's not a good example to use). – Eamon Nerbonne Mar 27 '11 at 04:26
  • Yes that is the sort of thread-safe that is useful (thread-safe shared count). I appreciate that thread-safe is a vaguely defined term. – James Mar 27 '11 at 05:55
0

"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.

0

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.

  • 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., copying/destroying shared pointers to the same thing in different processes at the same time. – James Apr 25 '11 at 16:59
  • Does the refcount even synchronize over interprocess? I'll have to check. – vagrantpostman Apr 25 '11 at 17:32
0

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?

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • Interprocess has a completely separate shared pointer implementation: the normal shared pointer is completely threadsafe in the regard that I care about: I am copying/destroying different shared pointers in different processes that point to the same thing (so which share a reference count). (`boost::shared_ptr` cannot , however, be placed in shared memory because it uses virtual inheritance, amongst other things. – James Apr 26 '11 at 10:23
  • With that clarification, then "yes" both versions (interprocess and not) of shared_ptr should be safe for that usage, as they use atomic operations as long as the proper flags are enabled (see, for example, the various platform specific headers included [here](http://www.boost.org/doc/libs/1_38_0/boost/detail/sp_counted_base.hpp)). – BeeOnRope Apr 27 '11 at 22:20