Recent

Author Topic: try..finally -- finally doesn't execute  (Read 5797 times)

dculp

  • Full Member
  • ***
  • Posts: 111
try..finally -- finally doesn't execute
« on: December 13, 2016, 11:43:07 am »
When I execute the following code an exception is raised at assign..close if rewrite(FileX) is commented. (I'm deliberately trying to raise this exception to debug a somewhat-related network problem.) After the exception, control isn't passed to finally -- the program just aborts and the window instantly closes. Since finally never executes the program doesn't pause and I can't see the exact exception that has been raised. (At first I though that either of readln, readkey, or delay weren't executing correctly but they seem to be OK for the numeric exception below.)

Is there a way to get the program to execute finally? (Although this is a particular example, I want to make sure that, in general, my try..finally blocks should work as expected.)

Note: If I comment the entire assign..close and uncomment X:= 1/0; (numeric exception) then finally is executed, as expected.

Lazarus 1.6, FPC 3.0.0
Windows 7 Pro

Thanks,
Don C.

Code: Pascal  [Select]
  1. program File_assign;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.    CRT, sysutils;
  7.  
  8. var
  9.    FileX: text;
  10.    X: double;
  11.  
  12. begin
  13.  
  14. try
  15. {$I-}
  16.  
  17.    assign(FileX,'FileX1');
  18. //   rewrite(FileX); // for this exception, finally is never executed
  19.    close(FileX);
  20.  
  21. //   X:= 1/0; // Raise numeric exception -- finally is executed properly
  22.  
  23. {$I+}
  24. finally
  25.    begin
  26.    writeln('End of program. ');
  27. //  readln;
  28.    readkey;
  29. //   delay(5000);
  30.    end;
  31. end;
  32.  
  33. end.
  34.  

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #1 on: December 13, 2016, 11:53:00 am »
Not using IOResut....? It is either IOResult or use exceptions
Code: Pascal  [Select]
  1. program File_assign;
  2.  {$mode objfpc}{$H+}
  3. uses
  4.    CRT, sysutils;
  5.  
  6. var
  7.    FileX: text;
  8.    X: double;
  9.  
  10. begin
  11.   try
  12. {$I-}
  13.     assign(FileX,'FileX1');
  14.     close(FileX);
  15. {$I+}
  16.    if IoResult <> 0 then writeln('Failed');
  17.   finally
  18.     begin
  19.       writeln('End of program. ');
  20.       readkey;
  21.     end;
  22.   end;
  23. end.
Outputs:
Code: [Select]
Failed
End of program.
Heap dump by heaptrc unit
8 memory blocks allocated : 4928/4944
8 memory blocks freed     : 4928/4944
0 unfreed memory blocks : 0
True heap size : 360448
True free heap : 360448

That is because of (Delphi docs) "If an I/O error occurs and I/O-checking is off, all subsequent I/O operations are ignored until the internal error flag is cleared. Calling IOResult clears the internal error flag. " In your case the error flag stays set. You MUST read IOResult to clear the error flag. Writeln and ReadKey are IO operations....

See also http://www.freepascal.org/docs-html/rtl/system/ioresult.html that says the same, but less clear.
Since I find this less clear I filed a bug report against the documentation on Mantis. (#0031113)
Although it is essentially correct, a little more explanation is warranted, I think.

Note that the finally block IS executed, but the IO operations are ignored, hence it looks like it isn't....

« Last Edit: December 13, 2016, 12:14:35 pm by Thaddy »
also related to equus asinus.

dculp

  • Full Member
  • ***
  • Posts: 111
Re: try..finally -- finally doesn't execute
« Reply #2 on: December 13, 2016, 12:15:10 pm »
OK - I removed the {$I . Now when I run this over my Windows network the writeln('End of program. ');
and readkey; are executed. However, the exception error message flashes briefly after readkey (too short for me to read). How can I display or capture the exception error message?

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #3 on: December 13, 2016, 12:35:17 pm »
You should not remove {$I+/-} you should read out IOResult. That clears the error flag and you will obtain the runtime error.
You can then simply display it with writeln. Like:
Code: Pascal  [Select]
  1. program File_assign;
  2.  {$mode objfpc}{$H+}
  3. uses
  4.    CRT, sysutils;
  5.  
  6. var
  7.    FileX: text;
  8.    I:Integer;
  9. begin
  10.   try
  11. {$I-}
  12.     assign(FileX,'FileX1');
  13.     close(FileX);
  14. {$I+}
  15.    I:= IOResult;  ///After assignment or use IOResult will be zero! So assign to a temp
  16.    if I <> 0 then writeln('Failed: ',I);  // 103 means IO error File not open
  17.   finally
  18.     begin
  19.       writeln('End of program. ');
  20.       readkey;
  21.     end;
  22.   end;
  23. end.

Or you would use exception handling:
Code: Pascal  [Select]
  1. program File_assign;
  2.  {$mode objfpc}{$H+}
  3. uses
  4.    CRT, sysutils;
  5.  
  6. var
  7.    FileX: text;
  8. begin
  9.   try
  10.     try  
  11.       assign(FileX,'FileX1');
  12.       close(FileX);
  13.     except
  14.       On E: EInOutError do
  15.         writeln(E.Message)
  16.       else
  17.         Raise
  18.     end;
  19.   finally
  20.     begin
  21.       writeln('End of program. ');
  22.       readkey;
  23.     end;
  24.   end;
  25. end.
« Last Edit: December 13, 2016, 12:37:25 pm by Thaddy »
also related to equus asinus.

Zoran

  • Hero Member
  • *****
  • Posts: 1473
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: try..finally -- finally doesn't execute
« Reply #4 on: December 13, 2016, 12:37:00 pm »
Not using IOResut....? It is either IOResult or use exceptions

Yes... however... we might have exception raised or not, but finally block must execute anyway?!  %)

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #5 on: December 13, 2016, 12:38:56 pm »
Yes, in both my examples above the finally block is always executed.
It is just that the code wasn't good, programmer error, although not an obvious one.

Also note that in the original code the finally block is also executed, just not any IO operations because the IO error flag was still set.
Just assign an object in the try block and free it in finally block, compile with -gh You will see it does not leak memory:proof the finally block gets executed.

QED:
Code: Pascal  [Select]
  1. program File_assign;
  2.  {$mode objfpc}{$H+}
  3. uses
  4.    CRT, sysutils;
  5.  
  6. var
  7.    FileX: text;
  8.    X: double;
  9.    I:Integer;
  10.    AnObject:TObject;
  11. begin
  12.   try
  13.     {$I-}
  14.       AnObject := TObject.Create;
  15.       assign(FileX,'FileX1');
  16.       close(FileX);
  17.    {$I+}
  18.    I:=IOResult;
  19.    if I <> 0 then writeln('Failed: ',I);
  20.   finally
  21.     begin
  22.       AnObject.Free;
  23.       writeln('End of program. ');
  24.       readkey;
  25.     end;
  26.   end;
  27. end.
Output:
Code: [Select]
Failed: 103
End of program.
Heap dump by heaptrc unit
9 memory blocks allocated : 4932/4952
9 memory blocks freed     : 4932/4952
0 unfreed memory blocks : 0
True heap size : 360448
True free heap : 360448

Proof the finally block is executed....
« Last Edit: December 13, 2016, 12:50:34 pm by Thaddy »
also related to equus asinus.

Zoran

  • Hero Member
  • *****
  • Posts: 1473
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: try..finally -- finally doesn't execute
« Reply #6 on: December 13, 2016, 12:48:18 pm »
Also note that in the original code the finally block is also executed, just not any IO operations because the IO error flag was still set.
Just assign an object and free it in finally block, compile with -gh You will see it does not leak memory:proof the finally block gets executed.

But dculp said:

Since finally never executes the program doesn't pause and I can't see the exact exception that has been raised. (At first I though that either of readln, readkey, or delay weren't executing correctly but they seem to be OK for the numeric exception below.)

Is there a way to get the program to execute finally? (Although this is a particular example, I want to make sure that, in general, my try..finally blocks should work as expected.)

@dculp: Can you please test the original code again and please confirm if finally block really doesn't execute, then report this bug. If it is true, then it sounds like a serrious error in FPC.

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #7 on: December 13, 2016, 12:51:59 pm »
@Zoran
That's not true, finally is always executed. I posted proof. above, but our replies crossed. No buts, no ifs, finally is executed. Always.
Of course you can only test this without IO operations, like creating and free an object. If the memory is OK, the finally is executed.

Like my QED example. If you comment out the free, it leaks memory, by the way  >:D >:D >:D
But you MUST read IOResult as per documentation. Otherwise the behavior is undefined.

If you do not do that both file operations will leak 40 bytes intotal, but all objects you care to allocate gets free'd. Just add more objects to the code ;)

Or add a break point in the debugger at AnObject.Free of course....
« Last Edit: December 13, 2016, 01:06:25 pm by Thaddy »
also related to equus asinus.

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #8 on: December 13, 2016, 01:14:20 pm »
Of interest to Zoran, but probably not for OP dculp, it is not really relevant here, but:
Jonas just informed on the bugtracker that readkey (writeln is...) is not a standard IO operation and should always be executed.
The rest of my proof still stands. It is programmer error. With my examples he should be able to solve it properly..
« Last Edit: December 13, 2016, 01:16:56 pm by Thaddy »
also related to equus asinus.

Zoran

  • Hero Member
  • *****
  • Posts: 1473
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: try..finally -- finally doesn't execute
« Reply #9 on: December 13, 2016, 01:21:21 pm »
Of course you can only test this without IO operations,

I don't understand. Without IO operations -- do you mean that you cannot test with simple writeln in finaly block? Why? Shouldn't it just get executed, regardless of errors above? And regardless of whether there are memory leaks or not.

Edit: I see now your new post:
Of interest to Zoran, but probably not for OP dculp, it is not really relevant here, but:
Jonas just informed on the bugtracker that readkey (writeln is...) is not a standard IO operation and should always be executed.
The rest of my proof still stands. It is programmer error. With my examples he should be able to solve it properly..

So, there really seems to be something special with IO...  %) I'm still puzzled.
« Last Edit: December 13, 2016, 01:23:21 pm by Zoran »

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #10 on: December 13, 2016, 01:29:54 pm »
So, there really seems to be something special with IO...  %) I'm still puzzled.
No, if IO operations are blocked by an IOResult flag <> 0 , you have to devise something different, like I did by allocating memory and free it. Then check if that memory gets free'd. But anyway it is documented and it is programmer error. Finally blocks will always execute even if an exception is raised.  With the exception of fatal system wide errors of course. Then nothing gets executed except your savings account ;)

And
No, just with ReadKey. About that one I am still puzzled too ;) But not of any concern to dculp for now. Let's wait what Jonas replies. BTW Read and Readln ARE IO operations. Just in case.
« Last Edit: December 13, 2016, 01:37:07 pm by Thaddy »
also related to equus asinus.

ASerge

  • Hero Member
  • *****
  • Posts: 1432
Re: try..finally -- finally doesn't execute
« Reply #11 on: December 13, 2016, 03:03:15 pm »
I don't understand.
It's simple. The IO error flag remains until the final Writeln in the finally block. Because at this IOCHECHKS is ON, then exception is thrown (in finally block), and as result readkey does not reach.

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #12 on: December 13, 2016, 06:39:48 pm »
I don't understand.
It's simple. The IO error flag remains until the final Writeln in the finally block. Because at this IOCHECHKS is ON, then exception is thrown (in finally block), and as result readkey does not reach.
No, it's not like that.
The IOResult flag remains until it is read/accessed. Until then all IO operations are blocked and will not execute.
Writeln is just one of those operations. If you look at the compiler sources you will see that IOResult only get cleared by reading it.
And it is cleared immediately on access so to work with it you have to assign it to an intermediate variable. most of the time
In the case of exceptions, it is pretty much the same story: the readkey will be executed, but readkey is not part of standard IO.
also related to equus asinus.

ASerge

  • Hero Member
  • *****
  • Posts: 1432
Re: try..finally -- finally doesn't execute
« Reply #13 on: December 13, 2016, 07:16:12 pm »
No, it's not like that.
Sorry, but I disagree with you. I even checked the code under debugger. Everything happens exactly as I described. After the last Writeln the error code is checked and because error code is not cleared up from the procedure "Close()" then exception raised and ReadKey does not reached.
I am talking about the source code in the beginning of the post.

Thaddy

  • Hero Member
  • *****
  • Posts: 9409
Re: try..finally -- finally doesn't execute
« Reply #14 on: December 13, 2016, 08:42:08 pm »
@ASerge
But that code is wrong. It contains a major error. Anyway, try that code and allocate an object in the try and free it in the finally block.
You will see that even with that error that object will be free'd as I already demonstrated. It is all about IO not being executed. All other code is.
As per my suggestion: put a breakpoint on the object.free. I don't know what you were debugging but it is wrong as per this screenshot with the invalid code.
The finally block is reached even with the invalid code. But the writeln inside the finally will throw the error again (a second one) , because IOResult is not cleared, and you can't recover from that since it is not protected by either finally or except... (or reading IOResult)... In that case the finally block will never finish indeed, but that is a  bug introduced inside the finally and completely unprotected, so to be expected.
« Last Edit: December 13, 2016, 10:54:50 pm by Thaddy »
also related to equus asinus.