Friday, November 3, 2006

try...finally.

There were quite a few interesting comments made to this post from the other day that seemed to indicate that there is a little confusion out there regarding exceptions.  More specifically, the try...finally construct.

I've seen some rather interesting twists on the use of try...finally that have made me pause and wonder why it was done that way.  When programming with exceptions, you always have to be aware that at nearly any point and without warning, control could be whisked away to some far-off place.  In many ways you have to approach and think about the problem very similarly to how you would in a multi-threaded scenario.  In either case, you always have to keep in the back of your mind two (or more in the multi-threaded case) potential code paths.  The first one is easy since that is the order in which you're writing the code statements that define the overall logic and intent of your program.  The other, and often forgotten code path is the exception execution path.

When an exception is raised (or "thrown" in the parlance of C++ and other similar languages) a lot of behind the scenes work is set into motion.  I won't be going into the details of exactly what is going on since that tends to be platform and language dependant.  I'll focus more on what happens from the programmers perspective and even more specifically the try...finally construct.  One way to think of the try...finally block is that it is the programmer's way of "getting in the way" of that secondary code execution path.  However, it is also unique in that it also "gets in the way" of the normal code execution path.  In the grand scheme of things, the try...finally block is one of the most used (and often mis-used) block types for programming with exceptions.  I'd venture to say that in the typical application the ratio of try...finally blocks to try...except blocks is on the order of 100:1.  But why use them at all and what are they good for?  It's all about resource management.  Memory, files, handles, locks, etc... are all examples of the various resources your application uses and interacts with.  The whole point of the try...finally block is to ensure an acquired resource is also properly handed back regardless of which execution path the application takes.

Let's look at some common misuses of the try...finally block and examine them more closely.

var
Obj: TMyClass;
begin
try
Obj := TMyClass.Create;
...
finally
Obj.Free;
end;
end;

There's a subtle problem here... Let's follow the normal execution flow first.  Control enters the try block, an instance of TMyClass is allocated and the constructor is called, control returns and the local variable, Obj, is assigned.  Some operations are done with the Obj instance (the "..."), then control enters the finally block and the Obj instance is freed.  So far so good, right?  I mean, the memory is allocated and freed and all is well with the heap along with any other resources needed by the TMyClass instance (assuming it is a properly designed class, that is).


Now let's look at the other possible flow of control.  Control enters the try block, an instance of TMyClass is allocated and the constructor is called.  Here is where something can go horribly wrong.  When programming with exceptions, the possibilities are nearly endless as to what can happen during the call to the constructor.  The most obvious is the case where the memory manager is unable to allocate enough space on the heap to hold the instance data for the new instance.   An "Out of Memory" exception is raised.  Hey, but that's OK because the finally block will get in the way of the the exception control flow, right?  Yep, that's right.  So memory was never allocated, the constructor was never called, local variable Obj was never assigned, and control is passed to the finally block which contains.... uh... Obj.Free;  Do you see it now?  Yep, that's right, the Obj reference was never properly set.  Because of that, the call to Obj.Free; is not good for the health of your application.  Chances are that another exception is going to be raised which will supercede the original exception (most likely a more fatal and nasty one).


So how do we fix this?  Hey I know!  What if we just made sure to pre-initialize the Obj reference to nil (Obj := nil;)?  Sure.  You could do that, but that is just adds another line of code to your function.  How can we arrange the above code to ensure that every time control is passed to the finally block regardless of which path of execution is used to get there?  It's actually very simple.  Here's the same block with that subtle change:

var
Obj: TMyClass;
begin
Obj := TMyClass.Create;
try
...
finally
Obj.Free;
end;
end;


But now the construction of the TMyClass instance isn't protected!  It doesn't have to be and let's examine why.  From some of my previous posts regarding exception safety and the use of Assigned, we alluded to the fact that while the constructor of an object is executing, if any exception is raised the destructor will automatically be called, the object will be freed and the exception is allowed to continue on its merry way.  There are essentially two main areas were things can go wrong.  The first is during the actual allocation of the memory for the instance.  If the memory manager is unable to find a block of free memory large enough to hold that instance, it will raise an "Out of Memory" exception.  Since the instance was never actually allocated, there is no need to ever execute the Obj.Free; line of code.  Since the try...finally block was never entered, Obj.Free will never be called.  The other place where things can go wrong is in the object's constructor.  In this case the memory manager allocated the memory and then control was passed off to the the constructor that would begin to setup the instance.   If something fatal happened along the way there, we know from those past articles that the destructor will automatically be called and the instance memory handed back to the memory manager for later re-use.  Since the object was already freed and, again, control never entered the try...finally block, the Obj.Free; line is never executed.  So in both of those scenarios, there was no need to touch the local Obj variable reference since the resources associated with the TMyClass instance were taken care of.  If the memory allocation succeeds, the constructor runs to completion, then control will return and the local variable Obj will be assigned after which control will then enter the try...finally block.  It is only at this point that you always want to make sure the TMyClass instance referenced by Obj is freed regardless of what happens next.


There are a lot of other interesting "twists" on the usage of try...finally and try...except blocks that I've seen over the years.  The above case stands out in my mind as the most common mistake made.  I'll address some of the other cases in following posts.  If you have any questions about certain common idioms and patterns that you're not sure are exception aware, post some comments and maybe I'll address them in future posts.

32 comments:

  1. Worth mentioning that with C++ (and thus C++Builder), try..finally is very rarely needed or used. The automatic destruction of stack-based objects when they're out of scope means thatyou can have it handled automagically via auto_ptrs or similar.


    I use the same approach with busy cursors, mutexes, and even BeginUpdate/Endupdate pairings.

    ReplyDelete
  2. I like the D approach to these problems. Those try/finally blocks can get quite messy. D supports "scope guarding", which basically lets you set code to run "on scope exit". This way the code for creating and releasing a resource can be grouped together.


    http://www.digitalmars.com/d/exception-safe.html

    (search for "scope(exit)")


    Maybe consider adding something similar to Delphi :)

    ReplyDelete
  3. I often use the wrapper object method Roddy mentioned (aka RAII idiom) as well.

    ReplyDelete
  4. You can get scope based object management in Delphi via the use interfaced objects. See this article for a primer:


    http://www.thedelphimagazine.com/samples/1415/1415.htm

    ReplyDelete
  5. The automatic destruction that C++ gives you is nice, sure. Also the various "tricks" of using an interfaced object are also somewhat "clever." The problem with many of those mechanism is that they're *not* ad-hoc and require you to go create a new class type or interface type when you need to manage some resource for which you don't already have something setup. C++'s auto_ptr is nice if you're doing memory block or object allocations. But what about file handles, synchronization objects, GDI handles, etc...?


    That was part of the reason I specifically did *not* mention all of those. Sometimes breaking one's train of thought and take a detour to bang out some resource manager class or interface is not good. By using an inline try...finally block, the workflow is preserved and the resource management needs are addressed.


    However, if you already have those other mechanisma at your disposal, then by all means take advantage of them... Although the interface "trick" is a little opaque and tends to "obfuscate" things too much.

    ReplyDelete
  6. Impatient Delphi LoyalistNovember 3, 2006 at 3:45 AM

    Allen,


    It's a shame that great blog articles like these will never been seen again especially by those who need it the most (new Delphi programmers). It's also sad to see that the TurboExplorer site has been underdeveloped for 2 months now. Whatever happened to "the site where you can find everything Turbo: product information, articles, code samples, games, competitions and more!..."? It served as nothing more than a half baked marketing gimmick. The site could have been used to provide the needed resources and materials for new programmers to learn how to program in Delphi. What good is it to release a free software dev't tool, with the intension to lure new developers, if the logistical support to help new developers learn the language is not there? It's like the Internet before Google arrived..all the documentation and sample codes are scattered all over the place and you have to scavenge for it. Compound that with the frustration you get with the BDS Help and you end up turning off new users.


    IDL

    ReplyDelete
  7. Allen wrote: "C++'s auto_ptr is nice if you're doing memory block or object allocations. But what about file handles, synchronization objects, GDI handles, etc...?"


    Suitable classes for things like that are either available in the RTL, or are pretty trivial to write. And if your objects don't need to be heap-based, you also can avoid the auto_ptr thing. But I *do* appreciate having try..finally in C++Builder: I tend to use that in an 'ad-hoc' way if I'm in a real hurry, and later refactor the code to use RAII classes.

    ReplyDelete
  8. How about this code below. If Obj1 is created successfully but something goes wrong when creating Obj2, then the try...finally block will never get entered and Obj1 won't get freed!

    How would you do this? I must admit have a lot of code that look like this.


    var

    Obj1,Obj2: TMyClass;

    begin

    Obj1 := TMyClass.Create;

    Obj2 := TMyClass.Create;

    try

    ...

    finally

    Obj1.Free;

    Obj2.Free;

    end;

    end;

    ReplyDelete
  9. Patricio MoschcovichNovember 3, 2006 at 7:17 AM

    Fredrik:


    I believe you would have to next them such as:


    var

    Obj1,Obj2: TMyClass;

    begin

    Obj1 := TMyClass.Create;


    try

    Obj2 := TMyClass.Create;


    try

    ...

    finally

    Obj2.Free;

    end;

    finally

    Obj1.Free;

    end;

    end;

    ReplyDelete
  10. Patricio MoschcovichNovember 3, 2006 at 7:18 AM

    Sorry typo: meant "nest" them, not "next" them

    ReplyDelete
  11. Allan, your example makes it look like pointers are not initialized to nil by Delphi, but rather that you have to do this yourself. I always write my code so it would not matter, but I once knew someone who told me that all references were initialized to nil by Delphi. They tended to use the


    try

    obj := something.create;

    ...do stuff...

    finally

    obj.Free;

    end;


    construction that you mention.

    ReplyDelete
  12. Frederik,


    As Patricio pointed out, you should next the try..finally blocks.


    John,

    Normal pointer *local* variables are *not* initialized to 0 for you which is why you should move that code outside the try..finally block. For the "managed" types such as strings, dynamic arrays, variants and interfaces, they *are* initialized on entry to the function.


    Allen.

    ReplyDelete
  13. <<Normal pointer *local* variables are *not* initialized to 0 for you which is why you should move that code outside the try..finally block. For the "managed" types such as strings, dynamic arrays, variants and interfaces, they *are* initialized on entry to the function.>>


    What about global pointers/references? What about members of a class? Are they initialized to nil?


    I've always assumed that Delphi did not initialize any pointers/references to nil, but in the past I've known some programmers who claimed otherwise. Were they right, or was I?

    ReplyDelete
  14. John,


    Globals *are* initialized to 0. Class members *are* initialized. Only local variables (ie. stack variables) are *not* initialized. Unmanaged record fields are *not* initialized unless that record is declared as a global.

    ReplyDelete
  15. I have to admit, I have see a LOT of try/finally (and by extension try/except) pattern abuses. I believe that in the first month or two of my transition from TP 7 to Delphi 1 that I commited a few as I got used to the pattern.


    The compiler should probably check at the beginning of a try loop to see if any variables referenced in the finally or except loops are defined. (just log all local variables that are not assigned, and when the corresponding finally or except rolls by check any variables referenced and warn that they may not be assigned). This will probably correct most pattern abuses quickly.


    I usually nest my object creation Try/finally blocks, but sometimes it is just easier to create a block of objects and then do a block release. I know, bad, evil, but when it is just a few string lists, it can be disrupting to have to nest everything (and if an out of memory exception gets thrown there, hey, it's not likely to be recoverable anyways, the app 'll have to come down the hard way, if the machine can let it come down at all!) I expect as I migrate to Turbo Delphi in the future that this will change with the block completion speeding things up.


    Oh, and on the note out of memory, let's face it, if it happens, you are screwed. You would have to a LARGE allocation fail before it become recoverable. If you can't create string list, you probably can't create the exception object either, and you probably can't even bring up the task list to kill the app. Running out of memory in today's day and age requires that you ask for a LOT of memory (or that the memory manager has failed), either way, you are pretty screwed. Just make sure you aren't asking for 2 gigs of ram, and you should be good.


    That said, I know a number of people in past who WOULD try to allocate 2 gigs of ram...


    Someone refered to a "scope(exit)" syntax that is similar to C#'s using syntax, and that's already been abused HORRIBLY. The syntax saves typing, but tends to turn people's brains off a little more than normal. That said, I probably wouldn't mind seeing something like it in Delphi win32, providing it used FreeAndNil(variable) instead of just variable.free.

    ReplyDelete
  16. "If you can't create string list, you probably can't create the exception object either, and you probably can't even bring up the task list to kill the app."


    a) take a look at SysUtils.pas:


    { Exception handling routines }


    var

    OutOfMemory: EOutOfMemory;

    InvalidPointer: EInvalidPointer;


    EHeapException = class(Exception)

    private

    AllowFree: Boolean;

    public

    procedure FreeInstance; override;

    end;


    EOutOfMemory = class(EHeapException);


    procedure EHeapException.FreeInstance;

    begin

    if AllowFree then

    inherited FreeInstance;

    end;


    Out of memory and invalid pointer exception objects are preallocated and never get freed (till application shutdown). No memory has to be allocated to raise these exceptions.


    b) There are basically 2 reasons why you should get an EOutOfMemory error. Either there is no sufficiently large continuous section of unused address space within the 2GB address space your application has (it's quite possible for that to happen while your application has just a few MB allocated if you end up badly fragmenting your address space). Or somewhere a memory overwrite has messed up the heap manager and it feels sick to it's stomach and pukes up an out of memory exception.


    None of that will in any way affect other process on the system. So you'll be able to start taskman just fine.

    ReplyDelete
  17. I agree with IDL that this kind of information should be included in Delphi documentation.

    ReplyDelete
  18. Just to add, the reason why you see so much "bad" exception handling code is that probably it is not exaplained well enough in the documentation.

    ReplyDelete
  19. I figured the anwer to my question would be nested try..finallys but do you actually do this for small objects? What if you were creating 5 objects? This would make the code look "unnecessary" ugly, and as others have mentioned if you are getting exceptions creating small object like stringslists then leaking an instance of TStringList may not be your biggest problem.

    I guess if I was creating 2 objects of TEatALotOfMemory I would do nested try..finallys or maybe put the creating of the objects in the beginning of the try..finally block but initialize the variables to nil before creating them.

    ReplyDelete
  20. What's worse, here IS an example from the help:


    procedure TForm1.Button1Click(Sender: TObject);

    var

    fileStream: TFileStream;

    begin

    try

    (* Attempt to open a non-existant file *)

    fileStream := TFileStream.Create('NOT_THERE.FILE', fmOpenRead);

    (* Process the file contents... *)

    fileStream.Free;

    except

    on EFOpenError do ShowMessage('EFOpenError Raised');

    else

    ShowMessage('Exception Raised');

    end;

    end;


    I think they encourages incorrect structuring of try..finally blocks.

    ReplyDelete
  21. meant to say "that encourages" not "they encourages"

    ReplyDelete
  22. Mark,


    That example is of a try..except clause and was meant to demonstrate handling a specific type of exception. I agree it should have included a try..finally block, but I imagine that doing that would have distracted from what was being discussed.


    Allen.

    ReplyDelete
  23. Allen,

    First, your remarks concern Win32 only. "On the .NET platform, the CLR initializes all variables, including local variables, to 0."

    Secondly, I believe that all object variables must be implicitly initialized to nil (in Win32 case). Is there any clear reason to avoid such obvious thing? For instance, just read Delphi documentation:

    "Use Free to destroy an object. ...

    Unlike Destroy, Free is successful even if the object is nil; so if the object was never initialized, Free won't result in an error." Here I want to underline the last words - "if the object was never initialized, Free won't result in an error".

    ReplyDelete
  24. To Alexey:


    The reason is SPEED. Sometimes superfluous initialization of local variables will slow down a program distinctly.

    ReplyDelete
  25. Allen,


    Thank you for interesting articles. I think they can be added to the Delphi help file.


    Just look at PHP documentation: every one page has very useful "User contributed notes" which give additional information about subject.

    ReplyDelete
  26. David McCammond-WattsNovember 6, 2006 at 3:07 AM

    I love using interfaces to automatically free objects, close database connections, close queries, close files and close sockets. (I've helped build a set of libraries that relies on interfaces in that way).


    Unfortunately, .NET takes that option away. Delphi for .NET, like C#, VB.NET and most other .NET languages (C++ under .NET being a notable exception) does not support reference-counted interfaces. If you have an interfaced object that references a socket, for example, releasing the last reference does not close the socket. You have to explicitly close the socket yourself (such as via a call to Dispose). This not only means that you have to call Close/Dispose explicitly, you have to know when to call Close/Dispose (no relying on the release of the last outstanding reference closing automatically).


    For those who have relied on this aspect of interfaces in Win32 it's painful to give up--especially as someone who writes libraries for groups of other developers to use. With interfaced objects you have a built-in safety net. But without it, one guy doesn't close his database connections or queries he's executing in a loop and the company's database is brought to its knees. Everyone has to get it right every time--the library writer can't build this safety-net into the library. Application developers have to get it right, too. Sure, you don't have to worry about cycles, but you have to worry about non-memory resources.


    Yes, relying on automatic freeing of interfaced objects is very nice and helpful. Unfortunately, it's just not portable to .NET.

    ReplyDelete
  27. My "favorite" bad example of exeption code is when someone wries code that swallows any and all exceptions. It looks like this:


    try

    ...do something...

    except

    end;


    This is usually done when they have some problem that they want to avoid bubbling up, but rather than handling the specific error or error type, they swallow any exception. This makes debuggin a nightmare, because it ends up masking the real source of the problem and you end up with very unpredictable results.

    ReplyDelete
  28. What about 'with'? I assume the following is the same as the second construct you show:

    with TMyClass.Create do

    try

    ...

    finally

    Obj.Free;

    end;

    ReplyDelete
  29. ...oops meant this of course:

    finally

    Free;

    end;

    ReplyDelete
  30. Allen,


    That was a really great article. Thanks


    Eddie

    ReplyDelete
  31. var

    Obj1,Obj2: TMyClass;

    begin

    Obj1 := nil;

    Obj2 := nil;

    try

    Obj1 := TMyClass.Create;

    Obj2 := TMyClass.Create;

    ....

    finally

    Obj1.Free;

    Obj2.Free;

    end;

    end;


    anything wrong with that?

    ReplyDelete
  32. What I often miss is a try...except...finally statement.


    Very often I'd need something like:


    try

    try

    do something

    except

    handle exception

    raise;

    end;

    finally

    do cleanup stuff

    end;


    would look much clearer like this:


    try

    do something

    except

    handle exception

    raise;

    finally

    do cleanup

    end;


    @Will: This is fine. (Actually I do it myself that way very often if I have lots of objects to create/free in the same range.

    ReplyDelete

Please keep your comments related to the post on which you are commenting. No spam, personal attacks, or general nastiness. I will be watching and will delete comments I find irrelevant, offensive and unnecessary.