"C++ Concurrency in Action second edition" has an example of thread-safe stack implementation, and the following is the code for pop:
template <typename T>
std::shared_ptr<T> threadsafe_stack<T>::pop()
{
std::lock_guard<std::mutex> lock(m);
// `data` is a data member of type `std::stack<T>`
if(data.empty()) throw empty_stack(); // <--- 2
std::shared_ptr<T> const res(
std::make_shared<T>(std::move(data.top()))); // <--- 3
data.pop(); // <--- 4
return res;
}
The book has the following explanation for this code:
The creation of res (3) might throw an exception, though, for a couple of reasons: the call to std::make_shared might throw because it can’t allocate memory for the new object and the internal data required for reference counting, or the copy constructor or move constructor of the data item to be returned might throw when copying/moving into the freshly- allocated memory. In both cases, the C++ runtime and Standard Library ensure that there are no memory leaks and the new object (if any) is correctly destroyed. Because you still haven’t modified the underlying stack, you’re OK.
Does this explanation correct?
If data item's move constructor throws, and the data item is corrupted. Although this implementation avoid memory leak, the state of the stack has been damaged/modified (not strong exception safe), and latter read will get corrupted data. Right?
So it seems that this implementation works, only if the move constructor doesn't throw (or at least, even if it throws, it keeps the data item unmodified)?
It is assumed that the if the move constructor of T throws an exception it will make sure that the original object remains in its original state.
That is a general exception guarantee that move operations should satisfy, otherwise it is trivially impossible to make any guarantees about exception safety when the move operation is used.
If that is not supposed to be a precondition on the element type, the copy constructor can be used instead of the move constructor if the move constructor is not noexcept. The copy constructor should never modify the original object. There is std::move_if_noexcept as a replacement for std::move in the standard library to implement this behavior.
Obviously if the type is not copyable and the move constructor not noexcept we are back again at the original problem and then it will simply be impossible to make exception guarantees if T's move constructor doesn't make any.
Related
A recent question (and especially my answer to it) made me wonder:
In C++11 (and newer standards), destructors are always implicitly noexcept, unless specified otherwise (i.e. noexcept(false)). In that case, these destructors may legally throw exceptions. (Note that this is still a you should really know what you are doing-kind of situation!)
However, all overloads of
std::unique_ptr<T>::reset() are declared to always be noexcept (see cppreference), even if the destructor if T isn't, resulting in program termination if a destructor throws an exception during reset(). Similar things apply to std::shared_ptr<T>::reset().
Why is reset() always noexcept, and not conditionally noexcept?
It should be possible to declare it noexcept(noexcept(std::declval<T>().~T())) which makes it noexcept exactly if the destructor of T is noexcept. Am I missing something here, or is this an oversight in the standard (since this is admittedly a highly academic situation)?
The requirements of the call to the function object Deleter are specific on this as listed in the requirements of the std::unique_ptr<T>::reset() member.
From [unique.ptr.single.modifiers]/3, circa N4660 §23.11.1.2.5/3;
unique_ptr modifiers
void reset(pointer p = pointer()) noexcept;
Requires: The expression get_deleter()(get()) shall be well formed, shall have well-defined behavior, and shall not throw exceptions.
In general the type would need to be destructible. And as per the cppreference on the C++ concept Destructible, the standard lists this under the table in [utility.arg.requirements]/2, §20.5.3.1 (emphasis mine);
Destructible requirements
u.~T() All resources owned by u are reclaimed, no exception is propagated.
Also note the general library requirements for replacement functions; [res.on.functions]/2.
std::unique_ptr::reset does not invoke destructor directly, instead it invokes operator () of the deleter template parameter (which defaults to std::default_delete<T>). This operator is required to not throw exceptions, as specified in
23.11.1.2.5 unique_ptr modifiers [unique.ptr.single.modifiers]
void reset(pointer p = pointer()) noexcept;
Requires: The expression get_deleter()(get()) shall be well-formed, shall have >well-defined behavior, and shall not throw exceptions.
Note that shall not throw is not the same as noexcept though. operator () of the default_delete is not declared as noexcept even though it only invokes delete operator (executes delete statement). So this seems to be a rather weak spot in the standard. reset should either be conditionally noexcept:
noexcept(noexcept(::std::declval<D>()(::std::declval<T*>())))
or operator () of the deleter should be required to be noexcept to give a stonger guarantee.
Without having been in the discussions in the standards committee, my first thought is that this is a case where the standards committee has decided that the pain of throwing in the destructor, which is generally considered undefined behaviour due to the destruction of stack memory when unwinding the stack, was not worth it.
For the unique_ptr in particular, consider what could happen if an object held by a unique_ptr throws in the destructor:
The unique_ptr::reset() is called.
The object inside is destroyed
The destructor throws
The stack starts unwinding
The unique_ptr goes out of scope
Goto 2
There was to ways of avoiding this. One is setting the pointer inside of the unique_ptr to a nullptr before deleting it, which would result in a memory leak, or to define what should happen if a destructor throws an exception in the general case.
Perhaps this would be easier to explain this with an example. If we assume that reset wasn't always noexcept, then we could write some code like this would cause problems:
class Foobar {
public:
~Foobar()
{
// Toggle between two different types of exceptions.
static bool s = true;
if(s) throw std::bad_exception();
else throw std::invalid_argument("s");
s = !s;
}
};
int doStuff() {
Foobar* a = new Foobar(); // wants to throw bad_exception.
Foobar* b = new Foobar(); // wants to throw invalid_argument.
std::unique_ptr<Foobar> p;
p.reset(a);
p.reset(b);
}
What do we when p.reset(b) is called?
We want to avoid memory leaks, so p needs to claim ownership of b so that it can destroy the instance, but it also needs to destroy a which wants to throw an exception. So how and we destroy both a and b?
Also, which exception should doStuff() throw? bad_exception or invalid_argument?
Forcing reset to always be noexcept prevents these problems. But this sort of code would be rejected at compile-time.
I am reading "C++ Concurrency in action" book and trying to comprehend exception safety in thread-safe data structures (for example stack). Author states that to avoid race condition, pop should do both operations - pop and return item from the stack, however:
If the pop() function was defined to return the value popped, as well as remove it from the stack, you have a potential problem: the value being popped is returned to the caller only after the stack has been modified, but the process of copying the data to return to the caller might throw an exception.
Here is proposed solution:
std::shared_ptr<T> pop()
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw runtime_error("empty");
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
data.pop();
return res;
}
In this case we got invocation of copy constructor on make_shared line. If copy constructor throws an exception, stack is not yet modified and we are good.
However I don't see how it differs much from this snippet:
T pop2() {
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw runtime_error("empty");
auto res = data.top();
data.pop();
return res;
}
Here we have copy constructor invocation at data.top() line and move constructor on return. Again, if copy constructor throws an exception we are good since stack is not yet modified. Move contstructor is not supposed to throw exceptions.
Am I missing something? What is the benefit of returning shared_ptr comparison to returning (movable) T ?
If T is not movable, your code might end up doing multiple copy-constructions. Using a shared_ptr to allocate the copy on the heap might be more efficient in this case.
And even if T is movable, your code might still do multiple (potentially costly) move-constructions. Move-constructing and/or copy-constructing a shared_ptr is known to be relatively cheap compared to move- or copy-constructing any possible type T.
Of course your mileage may vary depending on the exact types, your compiler and on your operating environment and hardware.
move-constructor may throw (in general). std::shared_ptr<T> has a noexcept move constructor.
So in first case return res; cannot throw, but in second case return res; may throw (and data.pop() has already been called).
Is calling a d-tor inside move assignment operator good practice?
here some example code:
VectorList &operator = (VectorList &&other){
~VectorList(); // if this is not a good practice,
// I will need to paste whole d-tor here.
_buffer = std::move(other._buffer );
_dataCount = std::move(other._dataCount );
_dataSize = std::move(other._dataSize );
other._clear();
return *this;
}
Should I use this code, or should I use swap() with move constructed object?
~VectorList does more than run the code in the dtor body: it actually destroys the object.
After that, the storage is unused. You can construct a new object there using a constructor, but simply accessing members is going to either be undefined behaviour, or require language-lawyers to find the loophole that lets it be defined.
Even if defined, it is dangerous, as an exception thrown while an automatic storage object is destroyed is bad news. Plus if the assigned-to class is actually of derived type, the dtor call itself is UB!
Neither is a worthwhile approach. The benefits are too small.
The better alternative is copy-swap (which is at least easy to get correct: it can prevent some optimizations), or refactor out the 'clear' code from both the dtor and assignment. Then call it at both spots.
Scott Meyers says don't use swap(): http://scottmeyers.blogspot.sg/2014/06/the-drawbacks-of-implementing-move.html
Regarding your current implementation, it seems you can do it more simply. I imagine that the destructor actually deletes _buffer and does little else. If that's true, you should just replace your harder-to-reason-about explicit destructor call with delete _buffer.
template<class T>
T Stack<T>::pop()
{
if (vused_ == 0)
{
throw "Popping empty stack";
}
else
{
T result = v_[used_ - 1];
--vused_;
return result;
}
}
I didn't understand all of it, or rather I understood none of it, but it was said that this code doesn't work, because it returns by value, I am guessing he was referring to result, and that calls the copy constructor and I have no idea how that's even possible. Can anyone care to explain?
Unlike the code in the question's example, std::stack<T>::pop does not return a value.
That's because if the item type needs to be copied, and the copying throws, then you have an operation failure that has changed the state of the object, with no means of re-establishing the original state.
I.e. the return-a-value-pop does not offer a strong exception guarantee (either succeed or no change).
Similarly, throwing a literal string is unconventional to say the least.
So while the code doesn't have any error in itself (modulo possible typing errors such as vused_ versus v_ etc.), it's weak on guarantees and so unconventional that it may lead to bugs in exception handling elsewhere.
A different viewpoint is that the non-value-returning pop of std::stack is impractical, leading to needlessly verbose client code.
And for using a stack object I prefer to have a value-returning pop.
But it's not either/or: a value-returning convenience method popped can be easily defined in terms of pop (state change) and top (inspection). This convenience method then has a weaker exception guarantee. But the client code programmer can choose. :-)
An improvement within the existing design would be to support movable objects, that is, replace
return result;
with
return move( result );
helping the compiler a little.
↑ Correction:
Actually, the above deleted text has the opposite effect of the intended one, namely, it inhibits RVO (guaranteeing a constructor call). Somehow my thinking got inverted here. But as a rule, don't use move on a return expression that is just the name of a non-parameter automatic variable, because the default is optimization, and the added move can not improve things, but can inhibit an RVO optimization.
Yes, returning by value formally calls the copy constructor. But that's not a problem at all, because in practice, compilers will typically be able to optimize away the additional copy. This technique is called "Return-Value Optimization".
More than the return statement (which can work if the class is movable but not copyable, e.g. you can return std::unique_ptrs), the problem is the copy you do here:
T result = v_[used_ - 1];
To make this copy possible, the type T must be copyable (e.g. T should have public copy constructor - required by the above statement - and copy assignment operator=).
As a side note, throwing a string is really bad: you should throw an exception class, e.g.
throw std::runtime_error("Popping empty stack.");
or just define an ad hoc class for this case and throw it, e.g.:
class StackUnderflowException : public std::runtime_error
{
public:
StackUnderflowException()
: std::runtime_error("Popping empty stack.")
{ }
};
....
throw StackUnderflowException();
If I move-construct a from b, is it still necessary to destruct b, or can I get away without doing so?
This question crossed my mind during the implementation of an optional<T> template. Excerpt:
~optional()
{
if (initialized)
{
reinterpret_cast<T*>(data)->~T();
}
}
optional(optional&& o) : initialized(o.initialized)
{
if (initialized)
{
new(data) T(std::move(*o)); // move from o.data
o.initialized = false; // o.data won't be destructed anymore!
}
}
Of course, I could just replace the bool initialized with a three-valued enumeration that distinguishes between initialized, non-initialized and moved-from. I just want to know if this is strictly necessary.
Yes, it is still necessary to destruct b. A moved from object is a valid, constructed object. In some cases, it may even hold resources that still need to be disposed of. In generic code such as you show, T may not even have a move constructor. You may invoke a copy constructor instead in this case. So you can definitely not assume that ~T() is a no-op and can be elided.
Yes, you do still have to destruct them. One of the designs that can show this flaw is for example, observer-based patterns where one object keeps lists of pointers to another. Not running the destructor won't remove the pointer and the code will crash when it attempts to access an object that no longer exists.
The easier thing to do in your example is just to not set initialized to false in the moved-from object. The value is still defined to be in a valid state after being moved from, and the destructor of the rvalue you're referring to would clean it up with no further intervention.
I'd like to answer 'No' to your question but I'm not sure it's even the right question to ask. Consider the following:
{ // start of scope
T maybe_moved;
if(some_condition) {
T(std::move(maybe_moved));
}
// end of scope
}
T::~T() should obviously be called only once for the maybe_moved object. If a move constructor would call it, how would you make such innocuous code work?