Home > Software engineering >  Asynchronous Destruction and RAII in C
Asynchronous Destruction and RAII in C

Time:08-15

According to RAII when I destroy the object, its resources are deallocated. But what if the destruction of the object requires asynchronous operations? Without using RAII I can call close method of my class that will call necessary async operations and will keep shared_ptr to my object (using shared_from_this) in order to gracefully process callbacks from the async operations. After calling close I can remove my pointer to the object since I do not need it anymore - but I know that the object will not be removed until the async operations are executed.

But how can I achieve this using RAII? One of the possible solutions can be using a wrapper that when destructed will call close method of by object. But.. will it mean that my classes are RAII classes?

class Resource: public std::enable_shared_from_this<Resource> {
    int id;
public:
close() {
    some_async_functin([t = shared_from_this()] {
        std::cout << "Now I am closed " << t->id << std::endl;
    }
}



}

My solution:

class ResourceWrapper {
   std::shared_ptr<Resource> res;

   ~ResourceWrapper() {
       res.close();
   }
}

CodePudding user response:

An object o to be asynchronously destroyed with respect to the thread, T, in which it was created cannot itself be managed via RAII, because destruction of stack-allocated objects is inherently synchronous. If o is managed via the RAII model then thread T will execute its destructor when the innermost block containing its declaration terminates, until the destructor terminates.

If you do not want to occupy T with releasing o's resources (yet ensure that they are in fact released), then you must delegate the resource release to another thread. You can do that either

  • directly, by creating o dynamically with new and dispatching the corresponding free asynchronously (e.g. via std::async), or

  • indirectly with RAII, by having o's destructor dispatch the resource cleanup for separate asynchronous release (again, via std::async or similar).

The latter is indirect because it requires a separate object or objects to represent the resource(s) being cleaned up until the cleanup is complete -- especially so if there are callbacks involved or similar. You face the same issue with these objects that you do with o itself. There is nothing inherently wrong with this indirection, by the way. RAII still provides the benefits it usually does.

CodePudding user response:

You'll need two classes: AcquirableResource and AcquiredResource. You first create the AcquirableResource instance, which outlasts close (might even be used to reopen); then you have AcquiredResource from AcquirableResource which is the opened resource. This is fully RAII.

// define the AcquirableResource and AcquiredResource

int main() {
    AcquirableResource ares("filename.txt");
    // ... your code here ...
    {
        auto res(ares); // open
        if (!res) { /* error handler */ }

        // ... your code here ...

        // res goes out of scope, signals ares
        // ares closes file (can be async)
    }
    // ares is still valid here

    // ... your code here ...

    // ares goes out of scope, dtor close in sync
}
  • Related