Много лет назад, в KS-драйвере для portcls.sys (там используются объекты COM), сделал вот такой код:
| NonDelegatingRelease и деструктор |
| ULONG CUnknown::NonDelegatingRelease () {
Assert (m_lRefCount > 0);
LONG const R = InterlockedDecrement (&m_lRefCount);
if (R == 0) {
m_lRefCount++;
delete this;
return 0;
}
return ULONG (R);
}
CUnknown::~CUnknown () {
Assert (m_lRefCount == 1);
}
|
| |
NonDelegatingRelease я, по сути, слизал из примера stdunk.cpp в WDK:
| NonDelegatingRelease из stdunk.cpp |
| STDMETHODIMP_(ULONG) CUnknown::NonDelegatingRelease(void)
{
ASSERT(m_lRefCount > 0);
if (InterlockedDecrement(&m_lRefCount) == 0)
{
m_lRefCount++;
delete this;
return 0;
}
return ULONG(m_lRefCount);
}
|
| |
Зачем там инкремент счетчика перед удалением, я так и не понял — он встречается в некоторых реализациях IUnknown, с пояснением, что это якобы "reentrance protection", хотя непонятно, что и как оно может защитить. Тогда разбираься было некогда — оставил, как есть.
Все это прекрасно работало лет пятнадцать на разных процессорах, под виртуалками и без, а с месяц назад стал периодически срабатывать Assert (m_lRefCount == 1) в деструкторе, причем в двух разных драйверах, пусть и переделанных из общего кода. При этом драйверы падают в отладчик, где я всегда вижу единицу в m_lRefCount.
Release и деструктор всегда вызываются только из одного потока, никакого параллелизма там и близко нет. Смотрел в код — там нет никаких намеков на гонки, все строго последовательно.
Убрал инкремент (изменив, само собой, в assert единицу на нуль) — все, как и ожидалось, работает прекрасно. Но таки хочется понять, отчего мог срабатывать assert.
Компилятора я не менял, обновлений винды не ставил, виртуалки, в которых это проявлялось, тоже не трогал. Единственное, с чем могу хоть как-то связать — менял термопасту на процессоре (засохла напрочь), после этого ядра перестали сбрасывать частоту на средней/сильной загрузке, отчего немного изменились тайминги. Если бы при освобождении/удалении объектов имел место хоть какой-то параллелизм — сразу списал бы на него, но параллелизма там нет совсем.
Что это могло быть?
Может, у кого возникали схожие ситуации?