Условия несрабатывания try except

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

stanilar
постоялец
Сообщения: 289
Зарегистрирован: 09.03.2010 18:09:02

Сообщение stanilar »

xdsl писал(а):что такое вообще возможно

Такое не только вполне возможно, но и легко возникает и при общеупотребительной практике: если в конструкторе возникает exception, то переменная, которой присваивается адрес создаваемого объекта получит nil. А дальше все очень елементарно: в try вызываете метод несуществующего объекта, а в except этот же объект освобождаете.
xdsl
постоялец
Сообщения: 131
Зарегистрирован: 15.01.2009 12:49:03

Сообщение xdsl »

stanilar писал(а):
xdsl писал(а):что такое вообще возможно

Такое не только вполне возможно, но и легко возникает и при общеупотребительной практике: если в конструкторе возникает exception, то переменная, которой присваивается адрес создаваемого объекта получит nil. А дальше все очень елементарно: в try вызываете метод несуществующего объекта, а в except этот же объект освобождаете.
Это совершенно штатная ситуация, она меня не интересует.

Привожу пример:

Код: Выделить всё

try
 proc();
except
end;
writeln('hello');
Обратите внимание - блок except пуст, там в принципе не может возникнуть исключение.
Вопрос: что надо делать внутри proc, что-бы на экране вместо hello появилось access violation?.
Т.е. внутри proc() возникает исключительная ситуация, но по загадочным причинам программа игнорирует все промежуточные блоки except, попадая сразу в глобальный обработчик исключительных ситуаций.

Хохма вида

Код: Выделить всё

procedure proc();
begin
  writeln('access violation');
  halt(1);
end;
также не интересует.

Добавлено спустя 3 минуты 3 секунды:
По словам автора треда никаких внешних вызовов он не делает, dll не дергает, внешние процессы, как я понял, тоже не запускает.
SeZuka
постоялец
Сообщения: 209
Зарегистрирован: 05.09.2012 14:58:05

Сообщение SeZuka »

xdsl писал(а):Обратите внимание - блок except пуст, там в принципе не может возникнуть исключение.
Вопрос: что надо делать внутри proc, что-бы на экране вместо hello появилось access violation?.

Да кто вам сказал что у автора этот блок пуст??? Вы его код вообще видели?
xdsl
постоялец
Сообщения: 131
Зарегистрирован: 15.01.2009 12:49:03

Сообщение xdsl »

Он его не показывает. Тем не менее, привык считать программистов вменяемыми, пока они не докажут обратное. А вменяемый программист в блоке except недоступную память писать-читать не будет, утверждая при этом, что необрабатываемый access violation
Однозначно внутри MainMethod.
SeZuka
постоялец
Сообщения: 209
Зарегистрирован: 05.09.2012 14:58:05

Сообщение SeZuka »

Не обязательно писать-читать в недоступную память, чтоб получить очередной exception, есть тысяча других способов.
xdsl
постоялец
Сообщения: 131
Зарегистрирован: 15.01.2009 12:49:03

Сообщение xdsl »

SeZuka писал(а):Не обязательно писать-читать в недоступную память, чтоб получить очередной exception, есть тысяча других способов.
Без разницы, т.к. предполагается что исключения в блоке except нет.
SeZuka
постоялец
Сообщения: 209
Зарегистрирован: 05.09.2012 14:58:05

Сообщение SeZuka »

xdsl писал(а):привык считать программистов вменяемыми, пока они не докажут обратное. А вменяемый программист в блоке except недоступную память писать-читать не будет

Вы сами себе противоречите
xdsl писал(а):Мне хочется смоделировать такую ситуацию и убедиться, что такое вообще возможно. Вот что такое надо ухитриться сотворить в блоке try?
xdsl писал(а):Без разницы, т.к. предполагается что исключения в блоке except нет.

Т.е. вменяемый программист может сделать в блоке try except ошибку которая приведет к полному краху, а в блоке except end даже малейшую ошибку совершить не может?
xdsl
постоялец
Сообщения: 131
Зарегистрирован: 15.01.2009 12:49:03

Сообщение xdsl »

xdsl писал(а):Мне хочется смоделировать такую ситуацию и убедиться, что такое вообще возможно. Вот что такое надо ухитриться сотворить в блоке try?
xdsl писал(а):Без разницы, т.к. предполагается что исключения в блоке except нет.
Т.е. вменяемый программист может сделать в блоке try except ошибку которая приведет к полному краху, а в блоке except end даже малейшую ошибку совершить не может?
try..except состоит из двух блоков: блок try и блок except. Поэтому никакого противоречия нет. И вменяемый программист не будет убежденно заявлять, что ошибка обязательно в блоке try, не проверив блок except. Либо выложит на обозрение код, где это будет сразу выявлено. Кода нет, поэтому остается только гадать.

И да, за блоком except надо очень внимательно следить, ибо его конечно можно оградить вышележащим try..except, но тогда надо следить за вышележащим блоком except, который конечно можно оградить ... Это процесс бесконечен.

Добавлено спустя 6 минут 10 секунд:
А вообще все это флуд какой-то, не приближающий к результату.
Поэтому повторюсь:

Код: Выделить всё

try
proc();
except
end;
writeln('hello');
что надо делать внутри proc, что-бы на экране вместо hello появился результат срабатывания исключения access violation?.
Т.е. внутри proc() возникает исключительная ситуация, но по загадочным причинам программа игнорирует все промежуточные блоки except, попадая сразу в глобальный обработчик исключительных ситуаций.
hovadur
постоялец
Сообщения: 116
Зарегистрирован: 31.01.2013 15:50:41

Сообщение hovadur »

xdsl может у тебя другой случай. В блоке после except происходит исключение. У меня уже было такое.
xdsl
постоялец
Сообщения: 131
Зарегистрирован: 15.01.2009 12:49:03

Сообщение xdsl »

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

Код: Выделить всё

{$mode objfpc}
uses baseunix;

procedure catchalarm (signo:integer);cdecl;
begin
 writeln ('пойман алярм: ', signo);
// pbyte(nil)^:=0;
end;

procedure proc();
 var set1:sigset_t;
     act:sigactionrec;
begin
  fpsigfillset (set1);
  fpsigdelset (set1, SIGALRM);
  act.sa_handler := SigActionHandler(@catchalarm);
  fpsigaction (SIGALRM, @act, nil);
  writeln('Спим до получения SIGALRM');
  fpsigsuspend(set1);
  writeln('Проснулись после SIGALRM');
end;

begin
 writeln('Сигналить с помощью kill -SIGALRM ',fpgetpid());

 try
  proc()
 except
 end;

 writeln('Завершение работы');
end.

Здесь задаем свой обработчик сигнала SIGALRM и блокируем программу внутри proc() до получения этого сигнала. Если оставить генерацию исключения в комментариях (// pbyte(nil)^:=0;), то программа отработает штатно:

Код: Выделить всё

Сигналить с помощью kill -SIGALRM 4470
Спим до получения SIGALRM
Затем программа висит во вроде-бы безопасном блоке try, ждет сигнала. В параллельной консоли сигналим, как требует программа: kill -SIGALRM 4470 (4470 - пример, номер процесса у каждого будет свой и каждый раз разный). Получив сигнал, программа задействует его обработчик:

Код: Выделить всё

пойман алярм: 14
По окончании размораживает основной код:

Код: Выделить всё

Проснулись после SIGALRM
и отрабатывает после блока except

Код: Выделить всё

Завершение работы


Если раскомментируем pbyte(nil)^:=0;, то получим:

Код: Выделить всё

Сигналить с помощью kill -SIGALRM 5067
Спим до получения SIGALRM
пойман алярм: 14
Ошибка сегментирования
Ясно видно, что блок except не помог. Скажу больше, даже если обработчик сигнала представить следующим образом:

Код: Выделить всё

procedure catchalarm (signo:integer);cdecl;
begin
 writeln ('пойман алярм: ', signo);
 try
  pbyte(nil)^:=0;
 except
 end;
end;
То даже этот блок except не сработает, ошибка сегментирования гарантирована.

К сожалению, эта программа не совсем соответствует условию задачи, т.к. не срабатывает не только ближайший, но и глобальный обработчик исключений. И вместо ожидаемого runtime error 216, получаем сообщение, генерируемое осью
Аватара пользователя
STAKANOV
энтузиаст
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV »

мужики, а вы не перемудрили?

NTFS писал(а):должно при любом раскладе (если я не использую вызовы dll в MainServerMethod) дать мне безопасное выполнение. Т.е, FinalServerMethod выполнится всегда.

Код: Выделить всё

try
  try
    MainServerMethod()
  except
    on E:Exception do
      ..
  end
finally
   FinalServerMethod()
end
xdsl
постоялец
Сообщения: 131
Зарегистрирован: 15.01.2009 12:49:03

Сообщение xdsl »

STAKANOV писал(а):мужики, а вы не перемудрили?

Судя по всему - нет. В моем варианте try finally не помогает. Если у автора треда похожая гадость в MainServerMethod(), естественно с учетом специфики windows, то и ему не поможет.

Вариант с finally поможет, если у него в MainServerMethod() обычное исключение, а затем - повторное в области от блока except до FinalServerMethod() (хотя он вроде тельняшку рвет, что исключение - только в MainServerMethod()).
Аватара пользователя
STAKANOV
энтузиаст
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV »

xdsl писал(а):
STAKANOV писал(а):мужики, а вы не перемудрили?

Судя по всему - нет. В моем варианте try finally не помогает. Если у автора треда похожая гадость в MainServerMethod(), естественно с учетом специфики windows, то и ему не поможет.

Вариант с finally поможет, если у него в MainServerMethod() обычное исключение, а затем - повторное в области от блока except до FinalServerMethod() (хотя он вроде тельняшку рвет, что исключение - только в MainServerMethod()).


Потестил варинат. Точно валится. Думал, что исключения не возникают при такой ошибке, но например код:

Код: Выделить всё

program trytest;
{$mode objfpc}
uses sysutils;
begin
 try
  try
   writeln('Делай раз');
   pbyte(nil)^:=0;
  except
   on E:Exception do writeln('Exception:', E.Message);
  end;
 finally
  writeln('Делай два');
 end;
end.


честно выдает:
alex@platinum:~/tests$ ./trytest
Делай раз
Exception:Access violation
Делай два
alex@platinum:~/tests$


так же данный способ я использовал на работе в одной утилите в виндовсе, так вот она все ошибки выдает на memo в окошке и никогда не падала, а пользуются ей круглые сутки уже несколько месяцев.

Автору темы можно лишь посоветовать попробовать try .. finally - большая вероятность, что поможет.
NTFS
постоялец
Сообщения: 388
Зарегистрирован: 05.11.2007 13:57:50
Откуда: Краснодар
Контактная информация:

Сообщение NTFS »

Можно и finally применить. На самом деле (как выяснилось), в главном методе вызывалось черти-что, вплоть до процедур из внешних .so и двухуровненых потоков, так что падение программы в хлам было скорее вопросом времени. Тщательное планирование, разделение на бинарники и проверки на каждое сомнительное действие - вот это действительно может избежать сбору проблем на филейную часть.
А думать, что компилятор сможет сделать это за программиста - мягко говоря, наивно, как я убедился.
xdsl
постоялец
Сообщения: 131
Зарегистрирован: 15.01.2009 12:49:03

Сообщение xdsl »

STAKANOV писал(а):

Код: Выделить всё

program trytest;
{$mode objfpc}
uses sysutils;
begin
 try
  try
   writeln('Делай раз');
   pbyte(nil)^:=0;
  except
   on E:Exception do writeln('Exception:', E.Message);
  end;
 finally
  writeln('Делай два');
 end;
end.

честно выдает:
alex@platinum:~/tests$ ./trytest
Делай раз
Exception:Access violation
Делай два
alex@platinum:~/tests$

Скажу больше, он будет то-же самое выдавать вообще без try..finally

Код: Выделить всё

{$mode objfpc}
uses sysutils;
begin
  try
   writeln('Делай раз');
   pbyte(nil)^:=0;
  except
   on E:Exception do writeln('Exception:', E.Message);
  end;
  writeln('Делай два');
end.

т.к. исключение уже обработано.
Ответить