Martin Martin - 1 month ago 7x
C++ Question

Synchronous destruction through std::shared_ptr<T>::reset()

Consider the following simplified program modelling a real scenario where different users can make concurrent requests to the same resource:

#include <thread>
#include <memory>
#include <mutex>
#include <iostream>

using namespace std;

struct T {
void op() { /* some stuff */ }
~T() noexcept { /* some stuff */ }

std::shared_ptr<T> t;
std::mutex mtx;
std::weak_ptr<T> w{t};
enum action { destroy, op};

void request(action a) {
if (a == action::destroy) {
lock_guard<mutex> lk{mtx};
std::cout << "*t certainly destroyed\n";
} else if (a == action::op) {
lock_guard<mutex> lk{mtx};
if (auto l = w.lock()) {

int main() {
// At some point in time and different points in the program,
// two different users make two different concurrent requests
std::thread th1{request, destroy}; std::thread th2{request, op};

// ....

I am not asking if the program is formally correct - I think it is, but I have never seen this approach for guaranteeing a synchronous destruction of a resource shared via smart pointers. I personally think it is fine and has a valid use.

However, I am wondering if others think the same and, in case, if there are more elegant alternatives apart from the classic synchronization with
s and condition variables and from introducing modifications (e.g. atomic flags) to

It would be ideal if I could even get rid of the


Yes, it's fine. The reference counting in the shared_ptr is atomic and the locked copy stays in scope for the duration of the op, so the object can't be destroyed during the op.

In this case the mutex is not actually protecting the lifetime of T, but sequencing calls to op() and destruction. If you don't mind multiple concurrent calls to op(), or the destruction time being indeterminate (i.e. after the last running op() has completed) then you can do away with it, since std::shared_ptr<>::reset() and std::weak_ptr<>::lock() are both thread-safe.

However, I would advise caution as the author clearly meant for calls to op() to be serialised.