Mega Code Archive

 
Categories / Delphi / Examples
 

Using try finally to avoid resource leaks

This article discusses Delphi's try..finally construct, when to use, and how Castalia makes it easier to use. (Originally published at delphi-expert.com on March 16, 2004) In a line of business that has me examining a lot of source code, I sometimes notice common habits and trends in people's code. Sometimes these habits are good, like using assertions. Sometimes the habits are bad, like using labels and gotos. Sometimes, the code is simply missing some very important elements, either due to laziness or inadequate understanding on the part of the programmer who wrote the code. One of these constructs that is very powerful, but often neglected and sometimes misused, is Delphi's "try" construct. Over the next two weeks, we're going to look at what these constructs do and how to use them well, and how Castalia can help you use them effectively and not neglect them. This week, we will concentrate on the try..finally construct. Next week we will go into detail with try..except, including some history on the concept of exceptions. We will also make fun of java, who can teach us a lot with its over-the-top exception handling mechanism. Introduction Sometimes you need to ensure that certain things are done to complete a task regardless of errors that may have occured in the execution of the task. For example, if a routine takes control of a resource, it is usually necessary for the routine to release the resource even if errors occur in the process. For example, in the following code, a file F is opened, some text is written to it, and then it is closed. Failing to close the file would result in a resource leak and would be a first class bug. Reset(F); WriteLn(F, 'Hello, World!'); CloseFile(F); This code works just fine as long as each function executes normally, but if something abnormal happens, then there will be trouble. For instance, if Reset(F) is successful, but for some reason the call to WriteLn fails, the routine will exit without ever attempting to CloseFile(F). As a result, the file will never be closed, and a resource has been leaked. How do we solve this problem? WriteLn does not provide any error-checking mechanism like some routines do (more on that next week). It simply executes, and if something goes wrong, it raises an exception (more on that next week too). In a nutshell, an exception is an abnormal condition in the execution of a program that causes the flow of the program to be interrupted. What we need is a way to make sure that CloseFile(F) gets called even if an exception occurs. This is exactly what the try..finally construct is for. Consider the same code, but with the protected flow control of a try-finally block: Reset(F); try WriteLn(F, 'Hello, World!'); finally CloseFile(F); end; Now, we have ensured that CloseFile(F) will be called no matter what happens during the call to WriteLn. Now for a more formal definition, straight from the Delphi help: The syntax of a try..finally statement is try statementList1 finally statementList2 end where each statementList is a sequence of statements delimited by semicolons. The try..finally statement executes the statement in statementList1 (the try clause). If statementList1 finishes without raising exceptions, statementList2 (the finally clause) is executed. If an exception is raised during the execution of statementList1, control is transferred to statementList2; once statementList2 finishes executing, the exception is re-raised. If a call to the Exit, Break, or Continue procedure causes control to leave statementList1, statementList2 is automatically executed. Thus the finally clause is always executed, regardless of how the try clause terminates. Now that we have an understanding of the try..finally construct and when to use it, let's look at how Castalia can help. Code Templates Castalia provides a number of code templates that involve the try..finally construct. When first installed, Castalia includes the tryf and trycf templates. The tryf template creates a simple skeleton for a try block. To use the tryf template, simply type "tryf" in the editor and push the spacebar. The skeletal code will be inserted: try | finally | end; The cursor will be located in the appropriate place in the try clause. After creating the code in the try clause, press once to jump into the finally clause. Then create your cleanup code. the trycf template addresses a particularly common use of the try..finally construct in Delphi. This is the dynamic creation and destruction of objects. Bad example: MyStrings := TStringList.Create; MyStrings.Add('Hello'); MyStrings.Add('World'); MyStrings.SaveToFile('hello.txt'); MyStrings.Free; This is similar to the first example. A resource (memory) is being allocated - in this case by a constructor. The resource is being manipulated, then the resoruce is deallocated by the calling MyStrings.Free. If we did not call MyStrings.Free, the memory would not be deallocated, and we would be looking at yet another resource leak. Again, the problem is the same. If something goes wrong while manipulating the memory, the destructor is never called and the memory is never deallocated. Here is the appropriate code: MyStrings := TStringList.Create; try MyStrings.Add('Hello'); MyStrings.Add('World'); MyStrings.SaveToFile('hello.txt'); finally MyStrings.Free; end; Notice that MyStrings.Free has been moved into the finally clause, ensuring that no matter what happens in the try clause, the object will be freed. Castalia's trycf template provides a skeleton for this construct. To use the trycf template, type "trycf" in the editor and press the spacebar. The skeletal code will be inserted: | := |.Create; try | finally |.Free; end; The cursor will be located where the first | character is, and pressing will jump to each | in order. (The | characters will not appear in code, but are used here for the purpose of clarity). You should use the trycf template whenever you are creating objects dynamically. You can learn more about Castalia's code templates here. Refactoring Code templates are useful when we are writing new code, but don't help very much when we are modifying existing code in accordance with our new understanding of try..finally. Castalia's refactorings will help with that. The "Surround With..." refactoring allows you to select a block of code and place a try..finally block around it with just a few clicks of the mouse. Just select the code that goes in the try clause and click it with the right mouse button. From the "Refactoring" menu, choose "Surround With..." and finally choose "try..finally." Your code will be instantly indented and enclosed in a proper try..finally block. You can learn more about Castalia's automated refactoring facilities here. This week we have learned how to use Delphi's try..finally construct to avoid resource leaks and other flow control problems. We have also learned how Castalia can help us to use this construct more effectively with both new code and existing code. Next week we will look at try..except and learn how Castalia can help us write better exception handling code