Is this code correctly synchronized? - c++

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.

Related

Lifetime Extension of Temporary Object in Multithreaded Context

I have the following scenario:
void thread_func(const Widget& w) { /*does something here...*/ }
int main() {
Widget w{ /* some arguments */ };
std::thread t(thread_func, std::move(w));
t.detach();
// lots of other stuff down here ...
}
Now I know that temporary objects, when bound to const T& parameters in a function call have their lifetime extended until the completion of the expression in which the call occurs. But in this multithreaded context, does the "completion of the expression in which the call occurs" refer to the construction of t, or to the completion of thread_func, since thread_func is (at the very least, conceptually) called at the end of t's constructor?
In a scenario with a single thread the answer is clear:
Widget h() { return Widget{/* some arguments */}; }
void g(const Widget& x) { /* do something */ }
void f() {
g(h());
}
the lifetime of the temporary within the call to g lives until g has finished executing, since execution pauses within f, begins in g, and only resumes in f once g has finished executing. In a multithreaded environment, the answer is more confusing to me because construction happens in a single thread, but the execution directly following construction happens concurrently.
This is not a lifetime extension situation. All temporaries live until then end of the full-expression, i.e. the outermost ;.
std::thread t(thread_func, std::move(w)); - this is a full-expression. So yes, the thread is starting when the expression is already ended.
Your thread is started with a reference to the original object, which must be keep alive for as long as it's needed in the thread.
Bonus notes:
You can't move into an lvalue-reference. Your std::move has no effect. But even if you changed the function to accept an rvalue reference, it would not change the fact that the original object needs to be kept alive externally. Remember that std::move does not move anything. What would change it though is if you simply accepted it by value.
void thread_func(Widget w) { /*does something here...*/ }
Detaching a thread is almost never needed. In your example the program will end when the main thread finishes, it will not wait for any other thread to finish. Consider joining your thread before main() returns instead.

Is it possible to create std future with blocking destructor without calling std async?

There is a remark on cppreference about the destructor of std::future that it will block:
it may block if all of the following are true: the shared state was created by a call to std::async, the shared state is not yet ready, and this was the last reference to the shared state.
So, basically if I create future via promise.get_future(), it does not block in destructor.
Is there a way to create an std::future with blocking destructor from std::promise?
Universal Asynchronous Model provides the means to customize behavior of asynchronous methods.
For instance, a completion may be a callable object, which may be a lambda:
async_foo([](){ std::cout << "foo";});
std::cout << "bar";
This will provide barfoo
You can also use a future to get synchronization primitives like std::future.
async_foo(asio::use_future);
std::cout << "bar"; // this line should be invoked only after async_foo has been completed
However, this does not work, since std::future will not block like it will in:
std::async(std::launch::async, &foo);
std::cout << "bar"; // this line will be invoked only after async foo has been completed
You could make a light wrapper for the std::future.
#include <future>
template <typename T>
class MyBlockingFuture {
std::future<T> future;
public:
MyBlockingFuture(std::future<T>&& f) : future(std::move(f)) {}
~MyBlockingFuture() {
if (future.valid()) {
future.wait();
}
}
};
Then add whatever get or wait methods you need.
Making std::future behave this way is not possible without modifying it.
Also note that there is no guarantee that the shared state held by a promise from std::async will block when destructed. The note only says that it may do so.
Blocking a future in a destructor is pretty fraught with danger in the first place, since it can throw an exception.
However even if that's not really your question, in case someone else lands here, it's fairly easy to write a wrapper that would do just that.
template<typename T>
struct FutureWaiter {
~FutureWaiter () { fut_.wait(); }
std::future<T>& fut_;
};
template<typename T>
[[nodiscard]] FutureWaiter<T> scoped_wait(std::future<T>& fut) {
return FutureWaiter<T>{fut};
}
int main() {
std::promise<int> x;
auto fut = x.get_future();
auto wait = scoped_wait(fut);
x.set_value(3);
}
You may want/need to change the ownership semantics a bit depending on your needs, but the principles should remain the same.

co_return vs. co_yield when the right hand side is a temporary

Background: This question arose when I was reading the source of cppcoro, specifically this line.
Question: Consider the following code:
#include <coroutine>
#include <stdexcept>
#include <cassert>
#include <iostream>
struct result_type {
result_type() {
std::cout << "result_type()\n";
}
result_type(result_type&&) noexcept {
std::cout << "result_type(result_type&&)\n";
}
~result_type() {
std::cout << "~result_type\n";
}
};
struct task;
struct my_promise {
using reference = result_type&&;
result_type* result;
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept {
return {};
}
task get_return_object();
void return_value(reference result_ref) {
result = &result_ref;
}
auto yield_value(reference result_ref) {
result = &result_ref;
return final_suspend();
}
void unhandled_exception() {}
};
struct task {
std::coroutine_handle<my_promise> handle{};
~task() {
if (handle) {
handle.destroy();
}
}
void run() {
handle.resume();
}
my_promise::reference result() {
return std::move(*handle.promise().result);
}
};
task my_promise::get_return_object() {
return { std::coroutine_handle<my_promise>::from_promise(*this) };
}
namespace std {
template <>
struct coroutine_traits<task> {
using promise_type = my_promise;
};
}
task f1() {
co_return result_type{};
}
task f2() {
co_yield result_type{};
// silence "no return_void" warning. This should never hit.
assert(false);
co_return result_type{};
}
int main() {
{
std::cout << "with co_return:\n";
auto t1 = f1();
t1.run();
auto result = t1.result();
}
std::cout << "\n==================\n\n";
{
std::cout << "with co_yield:\n";
auto t2 = f2();
t2.run();
auto result = t2.result();
}
}
In the code above:
Calling f1() and f2() both start a coroutine. The coroutine is immediately suspended and a task object containing the handle of the coroutine is returned to the caller. The promise of the coroutine contains result - a pointer to result_type. The intention is to make it point to the result of the coroutine when it finishes.
task.run() is called on the returned task, which resumes the stored coroutine handle.
Here is where f1 and f2 diverges:
f1 uses co_return result_type{} to invoke return_value on the promise. Note that return_value accepts an r-value reference of result_type, therefore bounding it to a temporary.
f2 uses co_yield result_type{} to invoke yield_value on the promise. Same as return_value, it accepts an r-value
In addition, yield_value returns final_suspend(), which in turn returns std::suspend_always, instructing the coroutine to suspend after yielding the value.
In both return_value and yield_value, result is set to point to the argument they received.
However, since final_suspend is also invoked (and its result awaited on) after a co_return, I expect no difference between using a co_return and a co_yield. However, the compiler proved me wrong:
with co_return:
result_type()
~result_type
result_type(result_type&&)
~result_type
==================
with co_yield:
result_type()
result_type(result_type&&)
~result_type
~result_type
Note that in the above output, the co_return version constructs a result, destroy it, and then move construct from it, invoking undefined behavior. However, the co_yield version seems to work fine, destroying the result only after move constructing from it.
Why is the behavior different here?
You are breaking a cardinal rule of C++: you wrote a function that takes a potential prvalue and store a pointer that outlives the function call that gave them a prvalue. Indeed, any time you see a function that takes an rvalue-reference (or a const-lvalue-reference) and stores a pointer/reference to that object which will outlive that function, you should consider that code to be highly dubious at best.
If a function takes a parameter as an rvalue reference, that means you're expected to either use it or move from it within that function call. Prvalues are not normally expected to outlive the function call they are passed to, so you either use them or lose them.
In any case, the behavior you're seeing is exactly what you're supposed to see. When a coroutine issues a co_return, it... returns. That means the main body of the coroutine block has exited. return_value is called while the block still exists, but once that is done, the coroutine block and all of its automatic variables (including parameters) go away.
This is why returning references to automatic variables from a normal function is a bad idea. This is just as bad an idea for co_return, even if you're indirectly shepherding that reference to the caller.
The co_yield version works (you still shouldn't do it, because that's not how you're supposed to treat prvalues, but it is required to work) because the co_yield statement itself is told to suspend by the return value of yield_value. This preserves the coroutine's stack, including any prvalues that were within the co_yield statement itself until the coroutine is resumed.
But again, you should do the move in the yield_value function, just as you would for rvalue reference parameters normally.

Is this use of std::atomic_thread_fence correct?

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.

Why is it impossible to move a variable to another std::thread

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();
// ^^^^^^^^^^^^
}