Когда встретился с проблемой nullptr! = NULL лицом к лицу

от автора

Многие из нас частенько читают статьи о багах и лучших практиках программирования, чтобы избежать возможных ошибок. Порой, ты просто знаешь, как не нужно делать, но на практике с реальной проблемой не встречаешься. Для меня такой была тема nullptr != NULL. Изначально NULL я использовала в системном программировании на Си. При переходе на C++, макрос NULL встречала только в WinAPI, в коде для null-указателей всегда использовала nullptr (все уже выучили, что nullptr != NULL). С реальными последствиями, где NULL используется вместо nullptr, мне не доводилось встречаться. Так было до замены boost::function на std::function в одном компоненте.

Рабочий день начинался стандартно с анализа новых аварийных дампов. Вот появилась пачка дампов одного сервиса. Выглядит обычно: произошёл вызов колбэка, когда объект уже разрушен. Скорее всего забыли отписаться от события при шатдауне, нужно это делать явно. Завели на это баг. 

Error(s): terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_function_call> >'   what():  call to empty boost::function

В течение трех дней подобная ситуация появилась ещё в нескольких сервисах. Значит, проблема не просто в отсутствии отписки. Нужно понять причину массовой проблемы и оценить масштаб. На днях должны сбилдить релиз‑кандидат, массовые крешдампы не допустимы. Уже несколько недель идёт обычный багфикс, никаких глобальных изменений не должно быть. Проверяю почту рассылок: на днях обновили версию одного компонента, от которого есть зависимость у всех упавших сервисов. Сроки релиза продукта и компонента разные, поэтому интеграция произошла довольно поздно. В компоненте много изменений, но все они относятся к замене boost::function на std::function.

Проверяю код отписки от событий в упавших сервисах. Вижу какой‑то непонятный код. Сначала не сразу соображаешь, что это и есть код отписки:

connection.SetCallback(boost::function<void(bool)>());

 что эквивалентно:

connection.SetCallback(NULL);

 В реализации connection при возникновении события, вызывается колбэк-функция.

Использование boost::function в компоненте:

void SetCallback(boost::function<void(bool)> callback) {    ... }  void ConnectionChanged(bool state) {     if (!_callback.empty()) {         _сallback(state);     // do something } }

После перевода компонента на std:

void SetCallback(std::function<void(bool)> callback) {    ... }  void ConnectionChanged(bool state) {     if (_callback != nullptr) {         _сallback(state);     // do something } } 

Причем если в качестве колбэка в SetCallback передать явно NULL, для случая с std::function ошибка обнаружится уже на этапе компиляции. А вот при отписке с использованием дефолтного конструктора boost::function() ошибка обнаруживается при исполнении кода. connection.SetCallback(boost::function<void(bool)>()) теперь не выполняет отписку, а устанавливает NULL как колбэка. В методе ConnectionChanged колбэк проходит проверку на nullptr, вызов фактически пустого колбэка приводит к исключению boost::bad_function_call. Причиной нарушения обратной совместимости стало то, что для boost::function конструктор по умолчанию возвращает NULL, а для std::function — nullptr.

Поиск по всей кодовой базе показал, что подобную форму отписки делают в десятках компонент. Если не поправить весь код, будет появляться много дампов: при выключении устройств и смене режимов работы (требуется перезагрузка сервисов). Перспективы так себе. Единственное предложение, которое можно было рекомендовать, это отложить релиз на неделю и все поправить в коде. Предложение одобрили. Все команды выделили людей для исправления кода.

Проблема была решена, но потребовала сдвиг сроков релиза. После этого случая компонент стали проверять на обратную совместимость (делали ревью кода) перед обновлением до новой версии. Теперь интеграция всегда откладывалась до начала следующего релиза.

Думаю, случай нечастый, но тоже можно встретить на просторах кодобазы.


ссылка на оригинал статьи https://habr.com/ru/articles/829810/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *