Анализатор обнаружил потенциальную ошибку, связанную с небезопасным использованием паттерна "блокировки с двойной проверкой" (double checked locking). Блокировка с двойной проверкой - это паттерн, предназначенный для уменьшения накладных расходов получения блокировки. Сначала проверяется условие блокировки без синхронизации. И только если условие выполняется, поток попытается получить блокировку. Таким образом, блокировка будет выполнена только если она действительно была необходима.
Рассмотрим пример с ошибкой:
static std::mutex mtx;
class TestClass
{
public:
void Initialize()
{
if (!initialized)
{
std::lock_guard lock(mtx);
if (!initialized) // <=
{
resource = new SomeType();
initialized = true;
}
}
}
/* .... */
private:
bool initialized = false;
SomeType *resource = nullptr;
};
}
В этом примере, оптимизация компилятором порядка назначений переменных 'resource' и 'initialized' может привести к ошибке. Т.е. в начале переменной 'initialized' будет присвоено значение 'true', а уже потом будет выделена память под объект типа 'SomeType' и проинициализирована переменная 'resource'.
Такая перестановка может привести к ошибке при доступе к объекту из параллельного потока. Получается, что переменная 'resource' будет еще не проинициализирована, а флаг 'intialized' уже будет выставлен в 'true'.
Одна из опасностей таких ошибок состоит в том, что часто кажется, будто программа работает корректно. Это происходит из-за того, что рассмотренная ситуация будет возникать не очень часто, в зависимости от архитектуры используемого процессора.
Дополнительные ссылки:
Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки при работе с многопоточными примитивами (интерфейсами запуска потоков на выполнение, синхронизации и обмена данными между потоками и пр.). |
Данная диагностика классифицируется как:
Взгляните на примеры ошибок, обнаруженных с помощью диагностики V1036. |