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?
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.
Related
Given the following code, Is it guaranteed to see the latest value 4 of a ?
int a;
mutex mtx;
void f() {
unique_lock<mutex> lck(mtx);
// read(a);
// is it guarantee it will see the value 4?
}
int main() {
a = 4;
thread(f);
}
Yes, it is guaranteed:
32.4.2.2 Constructors [thread.thread.constr]
Synchronization: The completion of the invocation of the constructor
synchronizes with the beginning of the invocation of the copy of f.
In other words, the construction of std::thread itself, which occurs in the original execution thread, synchronizes with the beginning of the invocation of the thread function. Or, in other words: everything that happens before std::thread gets constructed, in the original thread, is visible in the thread function.
My problem deals with the usage of different vectors at the same time. I know I can't expect the same vector to work in multiple threads simultaneously. I've broken down the program so it is easier to understand it. I have a ThreadClass class that has a constructor which just adds an element to the vector k and then calls a thread toCall which then outputs the size of the vector which is supposed to be one. The object of this class is created inside of a different vector inside of the main() function using vector's push_back member.
The output turns out to be 0. Sometimes I can get 1 as well. I can produce more of the number 1 if I switch to debug mode. I have tested this problem on a gnu C++17 compiler (Ubuntu 16.04) and a Visual Studio compiler (Windows 10). My question is now if this example shows that I should avoid using vectors in multi-threaded programs totally?
class ThreadClass
{
private:
std::vector<int> k;
std::thread thr;
public:
ThreadClass() {
k.push_back(27);
thr = std::thread(&ThreadClass::toCall, this);
}
void toCall() {
std::cout << k.size() << std::endl;
}
void close() {
if (thr.joinable())thr.join();
}
};
int main(){
std::vector<ThreadClass> lols;
lols.push_back(ThreadClass());
lols[0].close();
return 0;
}
The problem is that a value of type ThreadClass holds a reference to itself. Specifically, thr contains a copy of this.
When you copy or move such a value, e.g. when the temporary ThreadClass() is moved into lols, the duplicate holds a duplicate this ptr, i.e. it points to the old temporary, whose lifetime ends after the call to lols.push_back finishes.
We can replicate this problem without threads:
class Foo
{
private:
std::vector<int> k;
Foo* possibly_this;
public:
Foo() {
k.push_back(27);
possibly_this = this;
}
void toCall() {
std::cout << possibly_this->k.size() << std::endl;
}
};
int main(){
std::vector<Foo> lols;
lols.push_back(Foo{});
lols[0].toCall();
}
(For me, it prints 0 with -O0 on 7.3.1, but again, it's UB, so it could do anything on your machine.)
lols.emplace() will not help. If a std::vector resizes, then all pointers/iterators into it are invalidated. Unfortunately, you can't change the pointers stored in thr, so you're left with one solution: disable ThreadClass's copy and move constructors, like so:
//within the definition of ThreadClass
ThreadClass(ThreadClass const&) = delete;
In order to place ThreadClass in a container, you will need an additional level of indirection to allow the actual object of a value of type ThreadClass to have a stable location. Either std::list<ThreadClass> or std::vector<std::unique_ptr<ThreadClass>> will do the trick.
One issue is that your thread can call toCall before the constructor returns. It's not a good idea to go creating threads that call back into the object in the constructor. Defer the thread creation to some kind of start or launch function and call that after the constructor returns.
This is also a problem:
lols.push_back(ThreadClass());
Here, the destructor (of the temporary) can even run before toCall gets called! That's definitely not going to work. That's another really good reason not to create a thread in a constructor -- it makes temporary objects disastrous.
Let's say I have a member variable vector initialised in the constructor, and this vector is read (not written to anywhere else) in several other member functions. Would I need to protect access to the vector (including in the constructor), or is it guaranteed that the object will be fully initialised and flushed to main memory before it's used in other threads?
Let me provide an example:
class A
{
public:
A();
void f1();
void f2();
private:
std::vector<int> v;
};
A::A()
{
// do some setup work in v
v.push_back(1);
}
// called from thread1
void A::f1()
{
// some readonly work on v
for (auto i : v) {
// do something on i
}
}
// called from thread2
void A::f2()
{
// more readonly work on v
if (v.empty()) {
// do other work
}
}
Do I need to lock-protect v in A::A(), A::f1() and A::f2()?
An object is created by a single thread, so you never have to worry about thread safety when running code inside the constructor that touches member variables. However, if you are using static variables within the constructor then you may need to add some form of locking around the access.
There is an edge case where code within a constructor can be called by multiple threads, and this is when you are using either placement new. For example, let's say you've got a buffer somewhere, and you're going to allocate an object into it:
byte buffer[100];
Foo *foo = new (buffer) Foo;
Here, unless you are locking around the call to new then it's possible for two or more constructors to run in parallel as they're running against the same block of memory. However, this is a real specialized edge-case and would require special handling (eg locking around the placement-new construction).
An object is constructed by a single thread.
Other threads can access the object only by means of the instance reference.
In other words, the object's constructor will have finished its work before other threads call a method.
You therefore don't need to implement thread-safe code within a constructor.
Of course, in case another object is passed to the constructor as a parameter, eventual access to that object within the constructor, should be thread-safe.
As stated in the other answers, there is no point in implementing synchronization primitives in the constructer, but that doesn't mean you can't have a race, if you don't synchronize externally:
std::atomic<A*> g_ptr = nullptr;
void threadFun1() {
g_ptr.store(new A{}, std::memory_order_relaxed);
}
void threadFun2() {
A* l_ptr = nullptr;
while (l_ptr == nullptr) {
l_ptr = g_ptr.load(std::memory_order_relaxed);
}
l_ptr->f1();
}
In above code, you have a data race between the constructor of A and f1. The problem is that - without synchonization - from the point of view of thread2, g_ptr might be written before the object is completely constructed.
However, there is nothing you can do inside the constructor to prevent this kind of race. Instead you have to use external means of synchronization, like using non-relaxed memory ordering for the atomic load and store operations or starting thread2 from within thread1 after the global variable is set.
Take this code example below:
model.h
namespace Stackoverflow {
class Model {
public:
Model();
~Model();
std::vector<int> *integers() const { return _integers.get(); }; // read only
private:
std::unique_ptr<std::vector<int>> _integers; // registered before constructor
};
}
model.cpp
Stackoverflow::Model::Model() {
_integers = std::make_unique<std::vector<int>>(); // initialized
}
Stackoverflow::Model::~Model() {
_integers.release();
}
The private member "_integers" will be registered but not initialized until the constructor is being called by the caller.
Stackoverflow::Model stackoverflow;
When another thread want to access this vector, call the getter.
auto *vector = stackoverflow.integers();
The member will be fully initialized when the caller is actually asking for the vector.
I wonder if this code is fine or not:
#include <iostream>
#include <future>
struct Foo
{
Foo()
:m_a(0)
{
}
int m_a;
};
int main()
{
Foo f;
auto handle =
std::async( std::launch::async,
[](Foo* f) { std::cout << f->m_a << '\n'; } ,
&f
);
handle.get();
}
I believe m_a should be protected by a synchronization mechanism but my colleague says it is not necessary.
EDIT: To clarify my question: I am worried that the STORE operation from the constructor of Foo() happens after the LOAD operation from the other thread. I can't see what mechanism prevents the compiler from executing those instruction in this order.
EDIT: I believe an enthusiastic compiler could decide to inline the constructor, and delay the STORE operation for after the CALL operation to std::async. In which case the second thread could access m_a before it has been committed to memory.
Yes, this is correctly synchronised.
From the specification for async, C++11 30.6.8/5:
the invocation of async synchronizes with the invocation of f.
where f is the function argument to async (the lambda in your example).
Initialisation of f.m_a is sequenced before the call to async, and therefore before any access by the asynchronous function.
Furthermore,
the completion of the function f is sequenced before the shared state is made ready.
so the access must happen before the call to get() returns, and therefore before the object is destroyed.
What is the reason that you can't move an object to another std::thread? There are situations where it could be useful. For example:
You create a loop that accepts incoming socket connections. It would be nice to move incoming connections to another thread that will handle the connections. You don't need the connection anymore in the accept loop. So why should you create a pointer?
A small test case:
#include <iostream>
#include <thread>
using namespace std;
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest &pointertest) {cout << "Copy";}
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
void foo(Pointertest &&pointertest)
{
}
int main()
{
Pointertest pointertest;
foo(std::move(pointertest)); //Works
thread test(foo,std::move(pointertest)); //**cannot convert parameter 1 from 'Pointertest' to 'Pointertest &&'**
}
The std::thread constructor has to treat the arguments you give it somewhat differently from most forwarding functions.
The reason for this is due to questions of when the thread actually gets started. If the part of the function invocation that actually created the function argument gets run long after the thread object is created (which is entirely legal behavior), then the object that needs to be moved from may have long since been destroyed.
Just consider an altered version of your code:
std::thread some_func()
{
Pointertest pointertest;
thread test(foo,std::move(pointertest));
return test;
}
This is perfectly valid (the thread will be moved out of the function). However, there's a big problem. foo may not have been called yet. And since foo takes its parameter by reference, it now has a reference to a stack variable that has been destroyed.
That's bad. But even if foo took its parameter by value, it would change nothing. Because the actual movement into that parameter doesn't happen until some indeterminate time after the thread has been started. The attempt to move into the parameter will still use an rvalue reference to a stack variable that has been destroyed. Which again is bad.
Therefore, std::thread constructor does something different. It copy/moves the arguments you give it into internal storage (this is done on the current thread). Then it uses those values as arguments for the actual function call (this is done on the new thread).
According to the standard, the thread constructor should treat pass these internal variables to your functions as temporaries. The standard specifically states INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...), where the DECAY_COPY stuff happens on the original thread, while the INVOKE part happens on the new thread.
So it seems like your thread implementation isn't able to forward the non-copyable parameters through correctly. You should be able to pass a non-copyable type; the arguments are only required to be MoveConstructible.
So this would appear to be a bug in your implementation.
It is possible. Fixing the signature of your copy constructor makes it work for me:
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest const& pointertest) {cout << "Copy";}
// ^^^^^^
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
Also, do not forget to join your thread (or detach from it) before your thread object goes out of scope:
int main()
{
Pointertest pointertest;
thread test(foo, std::move(pointertest));
test.join();
// ^^^^^^^^^^^^
}