Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe to use auto_ptr to swap objects without locking in multithreaded environment?

I have a few data structures I allocate on the heap that are rarely modified but need fast read access. One example would be a struct allocated on the heap that is accessed by many threads very frequently in a read-only fashion. Periodically this struct needs to be re-written and to avoid locking contention I was wondering if it's safe to use auto_ptr to basically allow threads that have acquired a reference to keep processing until they're finished but allowing the writer to make a copy of the struct, rewrite it and quickly swap pointers with the new auto_ptr instance of the struct.

I got this idea from CopyOnWriteArrayList in Java and hoping to perform something similarly performant in C++.

like image 547
stgtscc Avatar asked Dec 27 '22 11:12

stgtscc


2 Answers

std::auto_ptr does not having any thread-safety guarantees when calling a non-const member such as reset() as you are suggesting. Moreover, neither does std::unique_ptr which you should consider as a replacement for auto_ptr as auto_ptr is effectively deprecated.

std::shared_ptr does provide such thread-saftey guarantees.

What you are typically guaranteed with shared_ptr is that the reference count is manipulated in an atomic manner, meaning that you are safe from data races when creating a copy of a shared_ptr, provided that nobody is currently modifying the shared_ptr at that very moment.

Consider the following use case for your shared_ptr. You have a global shared_ptr<string> sharedString; that is currently pointing to an instance of std::string. It is safe for many threads to call get() to retrieve a pointer to the string. They may also create their own shared pointer to it as so: shared_ptr<string> myString = sharedString;, provided that nobody is changing what the shared pointer points to.

Now, lets go back and fix the race condition that exists in the example. When it comes time to change what the global shared pointer points to, a thread may be making a copy of it -- that is a problem since it could leave the copy in an inconsistent state. So, we have to make sure that we change and copy it atomically.

Luckily for us, shared_ptr provides a number of specializations of the C++11 atomic operations: atomic_load, atomic_store, and atomic_exchange.

When making a copy of the shared_ptr, use atomic_load, and when updating the shared_ptr, use atomic_store.

Doing it this way is important for two reasons.

  1. The atomic_* operations provide a thread-safe way for you to make a copy of the shared pointer.
  2. Making a copy means that when the global shared pointer is changed, your copy still points to the old string so your code does not have to worry about having the data it is operated on change when it isn't expecting it.

Now, it is important to note that using thread-safe operations on a shared_ptr does not provide any thread safety to the type it points to. You always have to take care that the object pointed to is being used in a thread safe way.

like image 133
Sean Cline Avatar answered Apr 06 '23 00:04

Sean Cline


You can reassign an auto_ptr on the fly with reset(), by the way, it destroys the object instance formerly pointed.

However, auto_ptr is deprecated by unique_ptr, which have a swap() function which somewhat seems to stick to what you are seeking.

Keep in mind that these classes are not thread-safe.

like image 41
Lukior Avatar answered Apr 05 '23 23:04

Lukior