1

I want to initialize a field in a constructor and never change it afterwards. I want the guarantee that after the constructor finished, every read of the field reads the initialized value, no matter in which thread the read happens.

Basically, I want the same guarantees as a final field gives in Java.

This is what I tried:

#include <atomic>
#include <iostream>
#include <thread>

struct Foo
{
    Foo(int x) : x(x)
    {
        // ensure all writes are visible to other threads
        std::atomic_thread_fence(std::memory_order_release);
    }

    int x;
};

void print_x(Foo const& foo)
{
    // I don't think I need an aquire fence here, because the object is
    // newly constructed, so there cannot be any stale reads.
    std::cout << foo.x << std::endl;
}

int main()
{
    Foo foo(1);
    std::thread t(print_x, foo);
    t.join();
}
  • Is this guaranteed to always print 1 or can thread t observe foo.x in an uninitialized state?
  • What if instead of using the member initializer x(x) an explicit assignment this->x = x; is used?
  • What if x is not an int but some class type?
  • Does making x a const int change anything with regards to thread safety?
Tobias Brandt
  • 3,393
  • 18
  • 28
  • 1
    Coincidentally, you are passing a copy of `foo` to the newly spawned thread. If you really intend to pass a reference to `foo`, you need to pass it in a reference wrapper a la `std::thread t(print_x, std::cref(foo));` or - for a non-const reference - `std::thread t(print_x, std::ref(foo));`. – Casey Jun 10 '14 at 19:10

1 Answers1

3

Basically, if everything else is correct, there shouldn't be any problem. After initializing the field, and before accessing it in any thread, you need some sort of memory synchronization; that's clear. Otherwise, how can the other threads know that it is constructed. If you initialize it before starting the other threads, then creating the threads will ensure the necessary synchronization. (This only holds between the thread doing the creation, and the created thread. Other already running threads are not synchronization.) After that, as long as no thread modifies the value, no synchronization is needed.

With regards to your code, you don't need the fence, because the value is initialized before any of the other threads are created, and creating the thread ensures the necessary synchronization.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • So, if a thread is already running it could see `x` uninitialized after the constructor finished? – Tobias Brandt Jun 10 '14 at 18:52
  • Yes. In that case, you'd need synchronization in _both_ threads. (But how do you notify the running thread that the object now exists. That would probably provide adequate synchronization, and afterward, you wouldn't need any additional synchronizations.) – James Kanze Jun 10 '14 at 19:06
  • 1
    @TobiasBrandt There is no such thing as "before" or "after" without synchronization between threads. – Casey Jun 10 '14 at 19:06
  • @Casey Well expressed. – James Kanze Jun 10 '14 at 19:07
  • @Tobias basically the guarantee is the same as java: if the other thread sees the foo instance it is guaranteed to see the correct x value. But you still need synchronization of some kind to make sure threads see the new foo instance to begin with. – Voo Jun 10 '14 at 19:53
  • @cmaster Since you need synchronization to *get* the correct new instance, shouldn't that be enough to take care of the problem? If you don't synchronize the access to the instance field you have a problem, but I hope I made that clear. – Voo Jun 10 '14 at 20:59
  • @Voo I'm sorry, I was wrong. Somehow that synchronization (which must imply a read memory barrier) escaped my notice. – cmaster - reinstate monica Jun 10 '14 at 21:08