I am studying futures in my concurrent programming class. My professor has stated this in her slides:
"Valid" futures are future objects associated to a
shared state, and are constructed by calling one of the following functions:
async
promise::get_future
packaged_task::get_future
future objects are only useful when they
are valid. Default-constructed future objects are
not valid (unless move-assigned a valid future).
I can't understand the meaning of the above, especially the "unless move-assigned a valid future" part. Could someone please explain this in simple terms, and perhaps show some example code as well?
As stated in the std::future constructor:
Default-constructed future objects are not valid
This just means calling the default constructor for the object, something like:
std::future<int> f;
This will call constructor #1 which states:
Default constructor. Constructs a std::future with no shared state.
After construction, valid() == false.
As for the other part:
(unless move-assigned a valid future)
What is meant here is that the move constructor (future( future&& other ) #2) will be called which states:
Move constructor. Constructs a std::future with the shared state of
other using move semantics. After construction, other.valid() == false.
Basically, the state of other in this constructor is moved to this. That means if other.valid() == true then after the move constructor has returned other.valid() will be false and this.valid() will be true. If other.valid() was false to begin with then both will end up false.
std::future<int> fut; // fut.valid() == false, default constructor
std::future<int> valid_fut = std::async(std::launch::async, [](){ return 42; }); // obtain a valid std::future..
// valid_fut.valid() == true here
//now move valid_fut into new_fut
std::future<int> new_fut(std::move(valid_fut));
// new_fut.valid() == true
// valid_fut.valid() == false
To summarize:
Calling the default constructor for an std::future will result in valid() == false. Always.
Calling the move constructor for an std::future will result in valid() == true only if other.valid() was true before moving from it. False otherwise.
Related
UPDATE: To be even more explicit, and avoid misunderstandings: What I am asking is, in case of returning a named value, does the C++17 standard GUARANTEE that the move constructor will be invoked if I do std::move on the return value?. I understand that if not using std::move, compilers are allowed, but not required, to entirely elide copying and move constructors and just construct the return value in the calling function directly. That is not what I want to do in my example, I want guarantees.
Consider
class A; // Class with heap-allocated memory and 'sane' move constructor/move assignment.
A a_factory(/* some args to construct an A object */)
{
// Code to process args to be able to build an A object.
A a(// args); // A named instance of A, so would require non-guaranteed NRVO.
return std::move(a);
}
void foo()
{
A result = a_factory();
}
In this scenario, does the C++ standard guarantee that no copying will take place when constructing the result object, i.e. do we have guaranteed move construction?
I do understand the drawbacks of explicit std::move on a return value, e.g. in cases where class A is unmovable, we cannot do late materialization of temporaries and get 0 copy even without a move constructor in the class. But my specific question is this - I come from a hard real-time background and the current status of NRVO not being guaranteed by the standard is less than ideal. I do know the 2 specific cases where C++17 made (non-named) RVO mandatory, but this is not my question.
Is it safe to return std::thread from a function?
eg
std::thread getWindowThread(std::function<void()> f){
std::thread t(f);
return t;
}
std::function<void()> func = [](){};
std::thread winT = getWindowThread(func);
winT.join();
Yes, it's safe, as long as the function's return value is used to initialize or assign to another std::thread object.
The only precondition for std::thread's destructor is that joinable() is false:
[thread.thread.destr]
~thread();
Effects: If joinable(), calls terminate(). Otherwise, has no effects.
To be joinable(), the thread must have an ID not equal to a value-initialized ID:
[thread.thread.member]
bool joinable() const noexcept;
Returns: get_id() != id().
And std::thread's move constructor and move-assignment operator specify that a moved-from thread will have an ID equal to a value-initalized ID, and therefore will not be joinable():
[thread.thread.constr]
thread(thread&& x) noexcept;
Postconditions: x.get_id() == id() and get_id() returns the value of x.get_id() prior to the start of construction.
[thread.thread.assign]
thread& operator=(thread&& x) noexcept;
...
Postconditions: x.get_id() == id() and get_id() returns the value of x.get_id() prior to the assignment.
...
Since getWindowThread returns a function-local variable, its return value will be move-constructed from the returned object, and since winT will be move-constructed from the value returned by getWindowThread, your example is safe. The returned thread object is no longer joinable and can be destroyed.
However, if the returned value is not used to initialize or assign to another std::thread object, its destructor will call std::terminate() and crash your program. For this reason, I would recommend adding the [[nodiscard]] attribute to your function to ensure your compiler at least issues a warning in this dangerous situation (if you have access to C++17 features, otherwise there are compiler-specific extensions to enable the same behavior):
[[nodiscard]] std::thread getWindowThread(std::function<void()> f) {
std::thread t(f);
return t;
}
Yes, it's safe. That thread will be moved to winT.
Have a look std::thread's constructors. You can check that std::thread copy constructor (and copy assignment) has been deleted. See this to learn more about move constructor in C++.
When you return std::thread from the function, it's returned object is move-constructed and your variable winT will also be move-constructed as the function returns an object having value category prvalue.
std::thread winT = getWindowThread(func);
This should work and won't cause any undefined behavior. You might be thinking that once you return std::thread from the function, it will be destroyed and it's resources must be released and may be it's thread might be destroyed.
But as stated earlier, the object created in function will be move-constructed to construct winT object.
Detailed Information:
This link says that:
If *this has an associated thread (joinable() == true), std::terminate() is called.
So, to safely destroyed any std::thread object, you need to make sure it is not joinable. joinable() gives more insight on this:
So a default constructed thread is not joinable. A thread that has finished executing code, but has not yet been joined is still considered an active thread of execution and is therefore joinable.
So, when move constructor of winT constructs it, it swaps itself with object returned from your function. After that, temporary of your function will be destroyed and since, it won't be joinable, it is safe to destroy it.
I have the following C++(11) code:
#include <mutex>
void unlock(std::unique_lock<std::mutex> && ulock)
{
}
int main(void)
{
std::mutex m;
std::unique_lock<std::mutex> ulock(m);
unlock(std::move(ulock));
if (ulock.mutex() == &m || ulock.owns_lock())
{
throw std::runtime_error("");
}
return 0;
}
What I can't figure out is why the mutex is still held after the return from unlock(). My expectation is that the std::move() causes the lock to go out of scope (and become unlocked by the destructor) upon return from the call to unlock(). At the very least, it seems like the std::move() should have caused ulock to become "unbound" from the mutex m.
What am I missing?
void unlock(std::unique_lock<std::mutex> && ulock)
Here ulock is a reference. A special kind of reference, but still a reference. It is just an alias for another object. Its creation does not involve creation of a new object, or any kind of ownership transfer. Likewise, end of its lifetime does not lead to any destructor call, it just means that you lost the alias for referring to some other object (not that it matters, since the function is ending anyway).
If you want to transfer ownership, you need an object, so pass by value instead of by reference:
void unlock(std::unique_lock<std::mutex> ulock)
Now, you will have to move the original lock, since std::unique_lock does not support copy construction, only move construction.
I have the following member function:
Person ClassB::DoSomethingAndReturnPerson()
{
RAIIMutex myLock(&m_mutex);
return m_person;
}
RAIIMutex is an helper class that recieves a mutex and locks it in the constructor and releases in the destructor.
m_person is of type Person (something very small in size). Other functions in other threads might change this member.
I want to return m_person by value (return a copy) and of course I want to avoid the situation where the m_person being changed in another thread while it's being copied in the return so I've added the lock.
But what happens first ? Does the compiler first creates a copy of m_person or first calls the destructor of myLock ?
Theoretically it easly solvable by doing something like this :
Person ClassB::DoSomethingAndReturnPerson()
{
RAIIMutex myLock(&m_mutex);
Person tmp = m_person;
return tmp;
}
But I'm interested in knowing the answer to my question.
Thanks
The copy-initialization of the returned value will be processed before.
From the standard, [stmt.return]/3 (emphasis mine)
The copy-initialization of the result of the call is sequenced before
the destruction of temporaries at the end of the full-expression
established by the operand of the return statement, which, in turn, is
sequenced before the destruction of local variables ([stmt.jump]) of
the block enclosing the return statement.
Destructors of local objects are called after 'the last line of your code'. Here is a relevant quote from Standard (3.7.3 / 3):
If a variable with automatic storage duration has initialization or a
destructor with side effects, it shall not be destroyed before the end
of its block, nor shall it be eliminated as an optimization even if it
appears to be unused, except that a class object or its copy/move may
be eliminated as specified in 12.8
Consider the following snippet from a function:
...
std::string someStdString;
...
// someStdString is filled here.
...
StringWrapper wrapper {std::move(someStdString)}; // First std::move usage.
return std::pair<bool, StringWrapper> {true, std::move(wrapper)}; // Second usage.
The StringWrapper class implements all four copy and move constructors and assignment operators and its constructor is explicit.
Are both the first and second usages of std::move justified, or are one or both of them not necessary?
Yes, both of the std::moves are needed to ensure the move constructor gets invoked. The simple rule of thumb is "If it has a name, it is an lvalue." The only exception is when you are returning an automatic duration object which is about to go out of scope - this object is treated as an xvalue and is preferentially binds to the move constructor.
std::pair<bool, StringWrapper> foo() {
std::string someStdString;
// ... someStdString is filled here. ...
return {true, std::move(someStdString)};
}
I see little reason to include most of your code, let alone two moves.
A std::make_pair if you want to be explicit about returning a pair, or are in a half compliant compiler.
As written above, StringWrapper is implicitly creatable from std::string, so the implicit creation should not be confusing.
This is at efficient as it gets.
return std::make_pair(true, StringWrapper { std::move(someStdString });
Total number of moves: 1
Why? Because of copy elision of temporaries and Return Value Optimisaton.
It's also very readable.