Callbacks and `std::recursive_mutex` - valid use case?

412 views Asked by At

I have the following polymorphic interface:

struct service
{
    virtual void connect(std::function<void>(bool) cb);
      // Invoke 'cb' with 'true' on connection success, 'false' otherwise.

    virtual ~service() { }
};

Some implementations of service are synchronous:

struct synchronous_service : service
{
    void connect(std::function<void>(bool) cb) override
    {
        cb(true);
    }
};

Others are asynchronous:

struct asynchronous_service : service
{
    void connect(std::function<void>(bool) cb) override
    {
        _thread_pool.post([this, cb]{ cb(true); });
    }
};

I need to create a service wrapper, which is a service itself. This needs to be thread-safe and maintain some state under a mutex:

struct wrapped_service : service 
{
    state                    _state;
    std::mutex               _mutex;
    std::unique_ptr<service> _underlying;

    void connect(std::function<void>(bool) cb) override
    {
        std::lock_guard<decltype(_mutex)> guard{_mutex};
        // update `_state`

        _underlying->connect([this, cb]
        {
            std::lock_guard<decltype(_mutex)> guard{_mutex};
            // update `_state`
            cb(true);
        });

        // update `_state`
    }
}

If the _underlying->connect call is always asynchronous, then std::mutex will work fine. However, in the case that _underlying->connect is synchronous, the program will freeze.

This can be solved by using std::recursive_mutex instead of std::mutex, but it's generally known that it is a code smell.

Is this a valid use case for an std::recursive_mutex?

Or is the design flawed? Note that I have no control over the service interface.

1

There are 1 answers

0
Maxim Egorushkin On

There are two modes of callback: immediate and delayed. That requires the client to be ready for immediate callback and be re-entrant. That complicates the client implementation. If you make the callback to be always delayed that removes the need for the client to be re-entrant.