Synchronization in most derived destructors

I was running into a crash from calling a pure virtual method due to a race condition where other threads are still calling the methods of the derived class when it has already been destroyed. Here is its gist:

class Resource
{
protected:
    Resource();
    virtual ~Resource();

public:
    virtual void *lock_shared() = 0;
    virtual void unlock_shared() = 0;

    // Wait for all other threads to finish.
    void sync()
    {
        mutex.lock();
        mutex.unlock();
    }

protected:
    std::shared_mutex mutex;
};

Resource::~Resource()
{
    sync();
}

class Image : public Resource
{
public:
    Image();
    ~Image() override;

    void *lock_shared() override
    {
        mutex.lock_shared();

        return accessData();
    }

    void unlock_shared() override
    {
        processData();

        mutex.unlock_shared();
    }
};

      

Note that when an Image object is destroyed, all shared streams are expected to terminate. However, due to the order in which the destructor is called, the C ++ destructor Image::~Image()

is executed by the time sync()

in Resource::~Resource

, which means that the object is no longer of type Image

and we cannot call any of the Image

methods. Other threads still holding the lock will try to call Image::unlock()

when they are finished, resulting in a clean virtual call Resource::unlock()

and interrupting the program.

The obvious solution is simple: call sync()

in Image::~Image()

.

Unfortunately, this happens very often when I get a new class from Resource

or from Image

. I added assert()

in Resource::~Resource()

to check, which is try_lock()

always successful, but it doesn't help when I get from Image

.

So I was wondering if anyone knows of a more reliable way to prevent this race condition once and for all. Thank.

+3


source to share


1 answer


As an idea, if you feel comfortable using some factory to create class objects derived from Resource

, rather than explicitly instantiating them, you could make this factory return std::unique_ptr<DerivedResource>

with custom delete that will call p_obj->sync()

before deleting the owned instance p_obj

.



+4


source







All Articles