вторник, 9 марта 2010 г.

Ваш обработчик исключения тоже может возбудить исключение

Это перевод Your exception handler can encounter an exception. Автор: Реймонд Чен.

Рассмотрите такой код:
procedure ObliterateDocument;
begin
try
try
document.DestroyAll;
finally
document.Close;
document.DestroyExtensions;
document.DestroyPlugins;
end;
finally
document.Destroy;
end;
end;
А чуть позже у вас начинает ругаться Assert в document.Destroy, говоря, что в документе всё ещё есть активные плагины. Но у нас же есть вызов document.DestroyPlugins, и он находится в блоке finally, а весь смысл блока finally в том, что вы никак не можете избежать его выполнения.

Так почему же document.DestroyPlugins не выполняется?

Потому что ваш обработчик исключений тоже может возбудить исключение.

Обработчик исключения не активен во время обработки своего собственного блока обработки исключения (как finally, так и except). В результате, если document.Close возбудит исключение, то поиск обработчика для выполнения начнётся с блока вне текущего блока finally.

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

В нашем случае, исключение ловится внешним кодом, что приводит к пропуску остатка finally-блока. Прочие внешние finally блоки выполняются - потому что они содержат в себе finally-блок, который умер.

(Эта ошибка также существует в предлагаемой анонимом альтернативе к коду на кодах ошибок).

6 комментариев:

  1. Обработчик исключениЕ не активен

    ОтветитьУдалить
  2. Как раз столкнулся с этой проблемой. Какие есть варианты решения? Есть идея заключить блок finally, в котором могут возникнуть исключения, в дополнительный обработчик try..except:
    try
    ...
    finally
    try
    ...
    except
    HandleFinallyException;
    end
    end;
    Поискал в исходниках VCL, нигде подобной конструкции не видел. Разработчики видимо свято верят, что код в их блоках finally никогда не возбудит исключения?

    ОтветитьУдалить
  3. Идеологически правильное решение: любой деструктор и деструктор-like метод обязаны обрабатывать все свои исключения. Иными словами: очистка обязана быть успешной. Почему? Потому что вы не можете предложить ни одного способа восстановления после исключения в деструкторе или любом методе очистки.

    Наводящие вопросы: является ли объект, в котором произошло исключение в деструкторе, удалённым? Нет? Тогда, является ли он валидным? А как тогда удалять его повторно? И т.п.

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

    ОтветитьУдалить
  4. Спасибо за ответ. Итак, деструкторы (и прочие методы финализации) не должны выпускать исключения наружу. Осталось только убедить в этом безответственных товарищей, которые пишут плагины к моему приложению :) Потому что обидно, когда МОЯ программа рушится из-за того, что КТО-ТО некорректно написал деструктор...

    P.S. Давно и с удовольствием читаю Ваши блоги и статьи на КД; отдельное спасибо за статью про обработку ошибок :)

    ОтветитьУдалить
  5. Ну, я имел ввиду только ваш код, конечно же.

    Когда же речь заходит о взаимодействии со сторонним кодом, то часто приходится вставлять код, исправляющий чужие косяки и то, "что никогда не может произойти". А "убедить этих" - забудьте об этом. Это данность, которую нужно принять.

    Если протокол плагинов под вашим контролем - попробуйте добавить в инфу о плагине e-mail автора и автоматом высылать ему отчёт о проблемах с его плагином. Вот поймали исключение в его функции очистки - отправили отчёт. Ну это просто как пример.

    ОтветитьУдалить
  6. Примерно так и делаю. Только не по e-mail, а просто пишу информацию об исключении в лог. Вобщем, Вы полностью подтвердили мои соображения, убедился, что я иду правильным путем :)

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

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