Waiting for a cascading destructor

An x::destroy_callback_wait4 implements a way for a thread that destroys one object to stop, and then wait for some other object to get destroyed too, presumably by another thread. A typical use case is when a thread constructs an object, and this object constructs a reference to the second object. The first object is considered to be the owner of the second object, but the second object can also be used by other threads, and when the first object goes out of scope and gets destroyed, it's necessary to also wait for any other fleeting references to the second object to go out of scope so that the second object gets destroyed too.

This is similar to a guard object, except that the guard object gets instantiated on the stack, and waits for all guarded objects to get destroyed, when the guard object goes out of scope. x::destroy_callback_wait4 is a reference to a reference-counted object. Once it gets created and installed, explicit references to the object are no longer needed. A reference remains to it, since it's installed as a destructor callback to some other object. Whichever thread ends up destroying it, invokes the destructor callback, which then waits for the second object to get destroyed, before proceeding.

#include <x/destroy_callback_wait4.H>
#include <x/threads/run.H>

class parentObj : virtual public x::obj {

    class childObj;

public:
    parentObj()
    {
    }

    void start_child()
    {
        auto child=x::ref<childObj>::create();

        addOnDestroy(x::destroy_callback_wait4::create(child));

        x::run(child);
    }
};

x::destroy_callback_wait4's constructor takes a reference to a reference-counted object. Its destroyed() method, when and if it gets invoked, waits for the object's destruction, if it's not already destroyed. After the x::destroy_callback_wait4 gets installed as a parent's destructor callback, once the parent object goes out of scope and gets destroyed, if, previously, a child object got started, parent's destruction automatically waits for the child thread to exit, and for any other references to the child object to also go out of scope, and get destroyed, before the destruction of the parent object fully concludes.

Here's a slight variation on this theme:

        x::run(child)->addOnDestroy(x::destroy_callback_wait4::create(child));

By installing a x::destroy_callback_wait4 destructor callback on the thread's return value object, this has the effect of only waiting until the thread finishes execution. With no other references to the thread return value object, it goes out of scope and gets destroyed when the thread finishes, even if the child thread object remains in scope due to other references.

Note

Any thread where the last reference to the object that goes out of scope ends up invoking the destructor callback and waiting indefinitely for the other object to get destroyed, by some other thread. Threads and objects should be carefully designed, so that can actually happen, in pradtice.

Internally, x::destroy_callback_wait4 uses x::destroy_callback, which is where it gets the class name from.