Сергей Губанов писал(а):
На работе убил полдня на возню со странным багом. Есть двухсвязный список, организуется указателями first, last и у каждого элемента prev и next. Объекты в своих "конструкторах" сами себя в этот список добавляют, а в "деструкторах" из списка самоудаляются. Таким образом есть инвариант: в списке нет "трупов" (то есть объектов у которых был вызыван "деструктор"). Теперь начинается самое интересное. Во время работы программы в списке иногда всё-таки обнаруживается "труп".
Может быть Вы столкнулись с феноменом параллельной работы конструкторов и деструкторов?.. Поясню на вероятном примере:
Код:
Есть список А=В=С
Т=1 Вызван деструктор объекта В
Т=2 Вызван конструктор нового объекта D (объект В еще не удален и D объект его "видит" и пытается вставить себя перед ним - между А и В)
Т=3 Деструктор В делает A.Next := C; C.Prev := A;
Т=4 Конструктор D делает A.Next := D; D.Next := B; B.Prev := D; D.Prev := A;
[Примечание. Время Т=2 примерно равно времени Т=1, а время Т=4 чуть больше времени Т=3. Примерное равенство времен означает, что объекты "видят" одну и ту же карту памяти. "Чуть большее время" означает, что второй объект пишет в память после изменений, сделанных первым объектом, но при этом самих изменений он не видит, поскольку не перечитывает карту памяти].
Ссылка B.Next -> C, как правило не затирается... Таким образом, объект В себя удалил, но объект D снова поместил его в список (обратная связь не указывает на объекты В и D, они "видны", только при прямом проходе, но можно смоделировать ситуацию, когда и обратная связь будет содержать объекты B и D).
Если эта гипотеза имеет право на существование, то самый простой выход из ситуации, передать управление списком отдельному объекту, который сериализует (выполнит строго последовательно) обращения от списочных объектов на вставку, удаление и перемещение по списку. Что в общем-то правильнее... для значительного числа случаев.