vendredi 1 février 2019

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

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;
        // update `_state`

        _underlying->connect([this, cb]
        {
            std::lock_guard<decltype(_mutex)> guard;
            // 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.

Aucun commentaire:

Enregistrer un commentaire