Home > OS >  Thread-safe stack implementation
Thread-safe stack implementation

Time:09-22

"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)?

CodePudding user response:

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