среда, 28 октября 2015 г.

[prog.c++.bugs] Как я одну серьезную ошибку профукал

Вчера вечером, когда подготовка к релизу очередной версии SObjectizer-а не просто вышла на финишную прямую, а уже почти подошла к самой финишной черте, всплыла серьезная ошибка. Суть в том, что у каждого агента есть свой собственный direct_mbox. Этот direct_mbox представляет из себя что-то вроде хитрого прокси через который сообщения доставляются агенту напрямую. Важнейшим свойством direct_mbox-а должна была быть возможность корректно обрабатывать ситуацию, когда сообщение отсылается агенту, а агента уже и нет, он закончил свою работу и был уничтожен ранее. Так вот внезапно оказалось, что это свойство не работает! Т.е. в direct_mbox-е остается повисший указатель, при обращении к которому может происходить все, что угодно: от зависаний до крахов приложения.

Очевидно, что в процессе переделок реализации direct_mbox-ов и агентов, коих было не так уж и мало, вышеозначенное важнейшее свойство было утеряно. Но почему это не было отловлено ни одним из тестов (коих сейчас уже больше 170)? И вот тут-то выяснилась интересная история :)

Оказывается, что как только в SObjectizer было добавлено понятие direct_mbox-а, для проверки свойства корретной работы direct_mbox-а после разрушения связанного с ним агента, соответствующий тест под названием mpsc_mbox_stress был написан.

Но он был написан не как юнит-тест (т.е. запускаешь программу и сразу же видишь OK или FAIL), а как программа, которую нужно запускать вручную и смотреть, что именно произошло. Вероятно, это было сделано потому, что обращение по повисшему указателю не обязательно приводит к краху с SEGFAULT-ом. Можно зависнуть. А можно и вообще завершиться нормально.

В общем, не помню уже точно, в чем была причина, т.к. дело происходило больше года назад. Важно другое. В какой-то момент времени я решил, что mpsc_mbox_stress таки является юнит-тестом и включил его проектный файл в список проектных файлов юнит-тестов для того, чтобы он отрабатывал вместе с другими тестами при полном билде.

А он не отрабатывал, он просто компилировался, но не запускался. Потому, что в основной системе сборки для SObjectizer, Mxx_ru, для юнит-тестов нужно делать дополнительный проектный файл с именем prj.ut.rb. А его-то я не сделал.

Ошибка досадная. Но самое интересное не это. А то, что спустя несколько месяцев после появления mpsc_mbox_stress, Лёша Сырников, добавляя в SObjectizer поддержку CMake, таки посчитал mpsc_mbox_stress юнит-тестом и включил в CMakeLists.txt команды для запуска mpsc_mbox_stress-а в качестве юнит-теста.

И, что очень важно, при сборке SObjectizer посредством CMake, юнит-тест mpsc_mbox_stress таки запускался. Но отрабатывал нормально! Т.е. ни крахов, ни зависаний, ни каких-то других признаков. Как будто обращения по повисшим указателям не было от слова совсем. Но ведь они были!

Оказывается, что данный тест отрабатывает нормально, если он собирается GCC или Clang под Unix-ами (не важно, Linux, FreeBSD или MacOS). Под Unix-ом не падает. А вот под Windows падает. Причем как под VC++, так и под MinGW.

Только вот под Windows никто SObjectizer CMake-ом не собирает :)

Вот и получилось, что проблема была в SObjectizer уже фиг знает сколько времени, для обнаружения этой проблемы был тест, этот тест время от времени запускался, но проблему не находил. А на платформе, где он бы проблему гарантированно нашел бы, этот тест как раз и не запускался. Ну а про то, что данная проблема не обнаруживалась анализатором Coverity, даже и смысла нет говорить :)

По итогу, тест mpsc_mbox_stress был переделан так, чтобы SEGFAUILT возникал и под Unix-ами. После чего mpsc_mbox_stress таки был объявлен нормальным юнит-тестом. Реализация direct_mbox-а была исправлена. Попутно обнаружились ошибки в реализации еще нескольких юнит-тестов, но уже для других фич SObjectizer-а. Исправленная версия direct_mbox-а будет доступна в версии 5.5.9, релиз которой ожидается уже завтра. Если опять внезапно чего-нибудь не обнаружится ;)

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