40

Is unique_ptr thread safe? Is it impossible for the code below to print same number twice?

#include <memory>
#include <string>
#include <thread>
#include <cstdio>

using namespace std;

int main()
{
    unique_ptr<int> work;

    thread t1([&] {
        while (true) {
            const unique_ptr<int> localWork = move(work);
            if (localWork)
                printf("thread1: %d\n", *localWork);
            this_thread::yield();
        }
    });

    thread t2([&] {
        while (true) {
            const unique_ptr<int> localWork = move(work);
            if (localWork)
                printf("thread2: %d\n", *localWork);
            this_thread::yield();
        }
    });

    for (int i = 0; ; i++) {
        work.reset(new int(i));

        while (work)
            this_thread::yield();
    }

    return 0;
}
Valentin Milea
  • 3,186
  • 3
  • 28
  • 29

3 Answers3

46

unique_ptr is thread safe when used correctly. You broke the unwritten rule: Thou shalt never pass unique_ptr between threads by reference.

The philosophy behind unique_ptr is that it has a single (unique) owner at all times. Because of that, you can always pass it safely between threads without synchronization -- but you have to pass it by value, not by reference. Once you create aliases to a unique_ptr, you lose the uniqueness property and all bets are off. Unfortunately C++ can't guarantee uniqueness, so you are left with a convention that you have to follow religiously. Don't create aliases to a unique_ptr!

Bartosz Milewski
  • 11,012
  • 5
  • 36
  • 45
  • Interesting observation. I try to think when it makes sense to transfer unique_ptr by reference then. I mean when there is the case that you don't want to pass ownership. – Ghita Jul 14 '12 at 18:51
  • 1
    If 'passing between threads' means starting a new thread with unique_ptr argument, then you could replace unique_ptr with just about any type and still call it thread safe. Anything is thread safe when used correctly :) – Valentin Milea Aug 04 '12 at 15:43
  • 3
    u sure about this? my inner paranoid programmer tells me that if you pass the up between threads it is UB because you dont ensure seq_cst, aka if what up points to is modified by thread 1 and then up is passed to thread 2 then thread 2 might see stale values of pointed to stuff. – NoSenseEtAl Dec 31 '12 at 08:48
  • @ValentinMilea : It's not safe to pass an arbitrary pointer to a thread. You were probably thinking about passing values which have copy semantics -- these are indeed safe. – Bartosz Milewski May 08 '13 at 19:31
  • @BartoszMilewski : I meant there is nothing special about unique_ptr. You can safely pass arbitrary arguments when creating a thread, including plain pointers as long as the pointed data stays consistent: p = new A; thread t([](A *p) { ... }, p); p = nullptr; – Valentin Milea May 09 '13 at 08:02
  • 6
    -1. When people ask about thread safety they actually ask whether accessing **one instance** of an object from multiple threads at the same time is safe. In this sense `unique_ptr` is no safer than `T*`. – SnakE Dec 02 '16 at 12:51
  • *"Don't create aliases to a unique_ptr!"* According to this statement the move assignment operator of `std::unique_ptr` is using bad practices. So as a corrolary transferring ownership from one `std::unique_ptr` to another is bad practice just to follow this overly broad rule... – fabian Dec 31 '21 at 12:34
  • 2
    This answer makes no sense. You _can't_ pass a `unique_ptr` by value. – Paul Sanders Jul 04 '22 at 16:13
30

No, it isn't thread-safe.

Both threads can potentially move the work pointer with no explicit synchronization, so it's possible for both threads to get the same value, or both to get some invalid pointer ... it's undefined behaviour.

If you want to do something like this correctly, you probably need to use something like std::atomic_exchange so both threads can read/modify the shared work pointer with the right semantics.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • "Excitingly, when and if this happens, it will also result in double-freeing of the integer." It *might*. It might not. It might result in not freeing the integer at all. It might result in both of the moved versions having half of the pointer's value. It could do all kinds of things. – Nicol Bolas Jul 14 '12 at 17:29
  • True, I was making some assumptions about the architecture which aren't really warranted. – Useless Jul 14 '12 at 19:05
12

According to Msdn:

The following thread safety rules apply to all classes in the Standard C++ Library (except shared_ptr and iostream classes, as described below).

A single object is thread safe for reading from multiple threads. For example, given an object A, it is safe to read A from thread 1 and from thread 2 simultaneously.

If a single object is being written to by one thread, then all reads and writes to that object on the same or other threads must be protected. For example, given an object A, if thread 1 is writing to A, then thread 2 must be prevented from reading from or writing to A.

It is safe to read and write to one instance of a type even if another thread is reading or writing to a different instance of the same type. For example, given objects A and B of the same type, it is safe if A is being written in thread 1 and B is being read in thread 2.

SingerOfTheFall
  • 29,228
  • 8
  • 68
  • 105