четверг, 26 марта 2009 г.

Крутая бага в SObjectizer

Вчера выловил в SObjectizer серьезную ошибку, которая сидела в нем, наверное с незапамятных времен. Проявлялась она в ситуации, когда агент завершал обработку своего собственного сообщения, а в этот момент его на другой нити дерегистрировали. Суть в том, что после выхода из обработчика события для данной заявки уменьшались счетчики ссылок на саму заявку, на экземпляр сообщения и на агента. И вот, когда все счетчики ссылок обнулялись и оставалось только вызвать функцию уничтожения объекта-сообщения, как управление передавалось другой нити. Другая нить дерегистрировала агента и, поскольку ссылок на этого агента не оставалось, то агент и все его SObjectizer-овские внутренности уничтожались. Потом управление передавалось обратно первой нити, которая должна была уничтожить объект-сообщение. Но это уничтожение выполнялось с помощью вспомогательного объекта с описанием сообщения агента. А этот объект уже оказывался разрушенным вместе с самим агентом. Поэтому происходило обращение к повисшему указателю со вполне ожидаемыми последствиями.

Ошибки в многопоточных программах начинают напоминать мне важный закон, который был выведен физиками-экспериментаторами:

Все, что может сломаться, сломается обязательно. Все, что не может сломаться, сломается тоже.

Так и здесь. Вероятность возникновения такой последовательности событий, да еще в тот мизерный зазор по времени между обнулением счетчика ссылок и вызовом деструктора объекта, ну очень мала (как мне кажется). И тем, не менее, она воплотилась. Да еще в весьма важном проекте, который работает в режиме 24x7 под постоянно возрастающей нагрузкой.

PS. Я когда-то выдвигал в качестве недостатка языка C++ отсутствие в нем stack trace “из коробки”. Чтобы при сбое программы сама стандартная библиотека C++ выдавала точное место сбоя. Самое смешное, что в данном случае stack trace совершенно не помогал (я его получил таки с помощью StackWalker-а). Поскольку по stack trace не было ясно, что именно провоцировало сбой – долгое время я вообще грешил, что данная ошибка “наведенная”, что сбой в этом месте является порчей памяти где-то в прикладной логике.

Комментариев нет: