Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Необъяснимое поведение блока __try-__finally

37K
17 апреля 2008 года
leshich
7 / / 17.04.2008
Доброго времени суток!

Столкнулся с интересной ситуацией. Всегда думал, что операторы в блоке __finally исполняется всегда (конечно, кроме документированных случаев вроде завершения текущего потока в вышестоящем блоке __try).

Случайно обнаружил конструкцию в Borland C++ Builder 6.0, в которой также НЕ происходит исполнения операторов блока __finally:


Код:
__try
{
   try
   {
      throw("oops, an exception has occurred!");
   }
   catch(...)
   {
      return;
   }
}
__finally
{
   ShowMessage("now __finally block executes");
}


Здесь оператор "return" завершает текущую функцию, НЕ передавая перед этим управление блоку __finally!

Однако следующий пример работает как и положенно:

Код:
__try
{
   try
   {
      throw("oops, an exception has occurred!");
   }
   catch(...)
   {
   }

   return;
}
__finally
{
   ShowMessage("now __finally block executes");
}



Поясните пожалуйста, если кто сталкивался с чем-то подобным, нормальна ли эта ситуация. Заранее спасибо.
240
17 апреля 2008 года
aks
2.5K / / 14.07.2006
Наверно не совместимы одновременно C++ исключения и эти борландовские.
9.4K
17 апреля 2008 года
AIGrifon
165 / / 13.11.2007
Вполне возможен вариант: генерим исключение и переходим в catch, там встречаем return и выходим из функции. Во втором случае: получили исключение, перешили в catch, НО исключение произошло и во внешнем try, поэтому до return не доходим, а переходим сразу в finally.
37K
17 апреля 2008 года
leshich
7 / / 17.04.2008
Цитата: AIGrifon
Вполне возможен вариант: генерим исключение и переходим в catch, там встречаем return и выходим из функции. Во втором случае: получили исключение, перешили в catch, НО исключение произошло и во внешнем try, поэтому до return не доходим, а переходим сразу в finally.


Не думаю, что так происходит. В описании к оператору __finally указано, что никакой из оператов, включая return, goto, break и и.д., не может просто взять и выйти из тела __try, не передав перед этим управление в блок __finally (если конечно он присутствует). Ведь в этом и есть основная идея оператора __finally - всегда выполнять некий финальный код независимо от ситуации в __try. Это больше похоже на глюк... ((

37K
17 апреля 2008 года
leshich
7 / / 17.04.2008
Просто всё дело в том, что возникает серьезный вопрос о доверии к блоку __try-__finally в C++, если он не во всех случаях выполняет свои "обязанности"...
8.4K
17 апреля 2008 года
Zor
104 / / 23.04.2006
блин. вот гадство - действительно баг. теперь надо пачку кода переделать...
240
18 апреля 2008 года
aks
2.5K / / 14.07.2006
Цитата: leshich
Просто всё дело в том, что возникает серьезный вопрос о доверии к блоку __try-__finally в C++, если он не во всех случаях выполняет свои "обязанности"...



А в C++ такого и нету. Это спецефическая и нестандартная фича борландовского компилятора. Хотя встречаеся и в некоторых других. И как правило везде не рекомендуется исспользовать одновременно такой механизм исключений и стандартный С++. Я подозреваю, что тут тоже они тупо не могут работать вместе.

535
18 апреля 2008 года
Нездешний
537 / / 17.01.2008
Цитата:
спецефическая и нестандартная фича борландовского компилятора



Это SEH -- structured exception handling -- часть операционной системы.
Рихтер:

Цитата:
SEH — механизм операционной системы, доступный в любом языке программирования



Другое дело, что реализуется оно каждым компилятором по-своему.
Рихтер:

Цитата:
Основная нагрузка по поддержке SEH ложится на компилятор, а не на операционную систему. Он генерирует специальный код на входах и выходах блоков исключений (exception bloсks), создает таблицы вспомогательных структур данных для поддержки SEH и предоставляет функции обратного вызова, к которым система могла бы обращаться для прохода по блокам исключений Компилятор отвечает и за формирование стековых фреймов (stack frames) и другой внутренней информации, используемой операционной системой. Добавить поддержку SEH в компилятор — задача не из легких, поэтому не удивляйтесь, когда увидите, что разные поставщики no-разному реализуют SEH в своих компиляторах К счастью, на детали реализации можно не обращать внимания, а просто задействовать возможности компилятора в поддержке SEH
...
NOTE:
Не путайте SEH с обработкой исключении в С++, которая представляет собой еще одну форму обработки исключений, построенную на применении ключе вых слов языка С++ catch и throw
...
компилятор реализует обработку исключений С++ на основе SEH операционной системы. Например, когда Вы создаете С++ блок try, компилятор генерирует SEH-блок __try. С++ блок catch становится SEH-фильтром исключений, а код блока catch — кодом SEH-блока __except. По сути, обрабатывая С++ оператор throw, компилятор генерирует вызов Windows функции RaiseException, и значение переменной, указанной в throw, передастся этой функции как дополнительный аргумент

535
18 апреля 2008 года
Нездешний
537 / / 17.01.2008
Имхо, блоку catch(...) соответствует __except(EXCEPTION_EXECUTE_HANDLER). При возбуждении такого исключения система начинает глобальную раскрутку (см. схему)

Наверное, при конструкции
Код:
__try
{
      __try
      {
            ...
      }
      __except(EXCEPTION_EXECUTE_HANDLER) //catch(...)
      {
            return;
      }
}
__finally
{
      //не выполнится
}


оператор return в блоке __except останавливает глобальную раскрутку, и поэтому блок __finally не выполняется...

З.Ы. Проверьте на других компиляторах, у кого есть возможность!
Интересно, там такая штука ведет себя так же?
240
18 апреля 2008 года
aks
2.5K / / 14.07.2006
Цитата: Нездешний
Это SEH -- structured exception handling -- часть операционной системы.


Ну я не пользуюсь борландовским компилятором, потому не могу утверждать просто это их фича или реализация SEH. )

535
18 апреля 2008 года
Нездешний
537 / / 17.01.2008
Цитата:
просто это их фича или реализация SEH. )



Думаете, шифруются шпиёны? ;)

Потому и интересно, как поведут себя другие компиляторы, особенно от Micro$oft

37K
18 апреля 2008 года
leshich
7 / / 17.04.2008
Спасибо за резмышления.

Из моих наблюдений следует, что такая неправильная обработка блока __try-__finally происходит, если оператор return стоИт не непосредственно внутри __try-__finally, а в одном из вложенных блоков try-catch или __try-__finally. Получается, что нельзя здесь использовать вложенные конструкции...
240
18 апреля 2008 года
aks
2.5K / / 14.07.2006
В реализации SEH от MS они тоже не совместимы и всячески глючат насколько я помню. )
37K
18 апреля 2008 года
leshich
7 / / 17.04.2008
Чтобы окончательно убедиться, что такое поведение блока __try-__finally является глюком, провел еще один эксперимент:

Код:
void foo()
{
   __try
   {
      try
      {
         throw(1);
      }
      catch(...)
      {
         return;   // <--- вот где вредитель!
      }
   }
   __finally
   {
      // nothing to do
   }
}

void caller()
{
   while(1)
   {
      foo();
   }
}


Легко убедиться, что подобный циклический вызов "неправильной" функции foo() быстро приводит к утечке памяти (проверяется в Диспетчере задач). Однако синтаксически код верен и вполне законен. Утечка памяти происходит из-за того, что оператор "return" не дает выполниться функции _CatchClean(), которая ответственна за освобождение ресурсов после обработи исключения (см. ассемблерный код функции foo()). Поэтому, на мой взгляд, это явная недоработка компилятора.

Знатоки советуют, чтобы избежать этого, не делать вложенных конструкций, а разводить их по разным функциям.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог