Обратные вызовы и `std::recursive_mutex` - допустимый вариант использования?
У меня есть следующий полиморфный интерфейс:
struct service
{
virtual void connect(std::function<void>(bool) cb);
// Invoke 'cb' with 'true' on connection success, 'false' otherwise.
virtual ~service() { }
};
Некоторые реализации service
синхронны:
struct synchronous_service : service
{
void connect(std::function<void>(bool) cb) override
{
cb(true);
}
};
Другие асинхронны:
struct asynchronous_service : service
{
void connect(std::function<void>(bool) cb) override
{
_thread_pool.post([this, cb]{ cb(true); });
}
};
Мне нужно создать service
обертка, которая является service
сам. Это должно быть поточно-ориентированным и поддерживать некоторое состояние под 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`
}
}
Если _underlying->connect
вызов всегда асинхронный, тогда std::mutex
будет работать нормально Тем не менее, в случае, если _underlying->connect
синхронно, программа зависнет.
Это можно решить с помощью std::recursive_mutex
вместо std::mutex
, но общеизвестно, что это запах кода.
Это действительный вариант использования для std::recursive_mutex
?
Или дизайн несовершенен? Обратите внимание, что я не могу контролировать service
интерфейс.
1 ответ
Существует два режима обратного вызова: немедленный и отложенный. Это требует от клиента быть готовым к немедленному обратному вызову и повторному входу. Это усложняет реализацию клиента. Если вы выполняете обратный вызов всегда с задержкой, это устраняет необходимость повторного входа клиента.