Concurrency and parallelism
CP.22
Никогда не вызывайте неизвестный код при удержании блокировки (например, обратный вызов)
Причина
Если вы не знаете, что делает кусок кода, вы рискуете deadlock.
Пример
void do_this(Foo* p)
{
lock_guard<mutex> lck {my_mutex};
// ... делайте что-то ...
p->act(my_data);
// ...
}
Если вы не знаете, что делает Foo::act (возможно, это виртуальная функция, вызывающая член производного класса класса, ещё не написанного), оно может вызвать do_this (рекурсивно) и вызвать deadlock на my_mutex. Возможно, это будет блокироваться на другом mutex и не вернётся в разумное время, вызывая задержки для любого кода, вызывающего do_this.
Пример
Частый пример проблемы "вызова неизвестного кода" — это вызов функции, которая пытается получить заблокированный доступ к тому же объекту. Такая проблема часто может быть решена с помощью recursive_mutex. Например:
recursive_mutex my_mutex;
template<typename Action>
void do_something(Action f)
{
unique_lock<recursive_mutex> lck {my_mutex};
// ... делайте что-то ...
f(this); // f сделает что-то с *this
// ...
}
Если, как вероятно, f() вызывает операции на *this, мы должны убедиться, что инвариант объекта сохраняется до вызова.
Применение
- Отмечайте вызов виртуальной функции с удерживаемым не-рекурсивным
mutex - Отмечайте вызов обратного вызова с удерживаемым не-рекурсивным
mutex