January 26, 2018
In the Delphi language resource allocations and use is generally protected with try/finally blocks. But what's the best practice for protecting multiple resources? We've had some internal discussions and I'd like to share some of the considerations.
The Delphi language shares with many others a standard resource allocation pattern to make sure that in case of an exception the resources is properly released. Resources in this context are memory objects, files, operating system objects and handles, and the like. In Delphi, compared to other languages with a garbage collector, the relevance increased by memory management considerations.
Protecting a Single Object
In the most simple cases, you'd write code like:
allocate resource
try
use resource
finally
free resource
end;
A more specific example would be like:
A1 := TTest.Create;
try
A1.DoSomething;
finally
A1.Free;
end;
So far, so good. Notice that is an error happen during the constructor execution, Delphi will automatically execute the destructor for the partially initialized object (but this could be a topic for another blog post).
Protecting Two Objects: How NOT to Write the Code
The issue I want to focus on is how to write the same type of code if you need to allocate and dispose two resources. Here there are multiple options. What you shouldn't do (but is fairly common) is to write:
A1 := TTest.Create;
A2 := TTest.Create;
try
A1.DoSomething;
A2.DoSomething (A1);
finally
A2.Free;
A1.Free;
end;
The issue with this code is that in case the creation of A2 fails (and there could be many reasons), the A1 object would remain in memory. Simply pushing the second allocation within the try block is also not good:
A1 := TTest.Create;
try
A2 := TTest.Create;
A1.DoSomething;
A2.DoSomething (a);
finally
A2.Free;
A1.Free;
end;
With this code in case of a failure in object A2 constructor call, the finally block will try to Free an uninitialized object (the default value of a local object reference is undefined). This is why a possible solution is to set A2 to nil at the beginning -- as calling Free on a nil object has no effect. Or set all object reference to nil for simplicity and uniformity. Or write two nested try blocks to protect each of the resources.
Protecting Two Objects: A Tale of Three Solutions
This long introduction brings us to the point of this blog post. There are at least 3 different correct solution for the issue of protecting two resources in the same code block, as I just mentioned. Here are the three solutions in an image I "borrowed" from one of our RAD Studio R&D architects, Bruneau Babet.
Provided they are all correct in terms of proper resource management in all scenarios, which are the advantages and disadvantages of these 3 solutions? What is important to consider is that the 2 resources could be 3, or 4 or half a dozen.
Which One to Pick?
The first solution with the nested try blocks fairly clean but more verbose (more lines of code) and has additional nesting that could become an annoyance with multiple resources. Also, there is a runtime cost associated with try blocks, clearly limited but not zero.
The second solution has the least amount of lines of code code and the least amount of runtime cost. The only additional is setting A2 to nil. The code remains readable also with many resources. However, the code is "unbalanced" and it might be slightly confusing.
The third solution goes in the same direction, but it adds one extra technically useless assignment to nil (for A1), offering the advantage of being cleaner and more balanced, and likely more readable after all.
So what's the best solution? This is really hard to tell,. I personally mostly used #1 in my books, but at Embarcadero we tend to prefer the cleaner and faster ones (that is, #2 or #3) for library code. Interested in your opinions, of course.
posted by
marcocantu @ 12:43AM | 25 Comments
[0 Pending]
25 Comments
Try-Finally Blocks for Protecting Multiple Resources in Delphi
What about creating a method that receive A1 and create/destroy A2?
Comment by Daniel Sonda on January 26, 19:00
Try-Finally Blocks for Protecting Multiple Resources in Delphi
My pick is #3.
However, I would like Embarcadero to consider
extending the to simplify the syntax so we don't have
to write this boilerplate code over and over again.
Here is what I have in mind:
var
A1, A2: TTest;
begin
try (A1, A2) // compiler should initialize A1 and
A2 to nil behind the scenes
A1 := TTest.Create;
A2 := TTest.Create;
// make finally optional if the only the only thing
we want to do there is calling the Free on A1 and A2
// compiler automatically generate code to call
A1.Free; and A2.Free;
end;
Comment by Zenon on January 26, 20:03
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I like #3. It's a bit verbose, but the thing I like about it is it is very
explicit.
When someone else (or me) comes along later and needs to change
things (add another object, remove one, etc) the code almost instructs
them what to do because both objects are being treated consistently.
With #2, it works but it relies on the person understanding why it
works to explain why the code does different things to each object.
Cheers
Malcolm
Comment by Malcolm Groves
[http://www.code-partners.com ]
on January 26, 22:28
Try-Finally Blocks for Protecting Multiple Resources in Delphi
We’re using #3
Comment by Alfred on January 27, 09:02
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I have always used the first solution; a block for each
resource to be protected. I never thought about
changing it. However I admit that the number 3
solution is interesting. I think the number 2 is
confusing, although useful when the code needs to be
optimized to the maximum.
Comment by Roberto Nicchi on January 27, 11:43
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I prefer #3
Comment by Roberto Verrini on January 28, 10:27
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I'm C++ but I always use option 3.
In reality one often has many objects to construct,
and finally release.
It's readable, maintainable, and works as expected.
Sometimes an object may be reused several times within
the try/finally before destruction.
Where an Object may or may not be actually
constructed during the process, it is best to test for
existence inside the finally before cleaning it up.
In C++ one must also set it back to NULL.
Comment by Alan Taylor
[http://www.altor.com.au]
on January 28, 19:39
Try-Finally Blocks for Protecting Multiple Resources in Delphi
Our code standards requires #3. Just be aware that
that destructors shouldn't raise exception themselves,
or they need to be caught inside the finally block.
That can gets tricky. If you fear that, #1 is better.
IMHO it's also good "safe programming" technique to
set variables to valid values, especially in complex
code where a bug may leave them containing random data.
Especially when variables are used once, it would be
often even easier to use if Delphi would allow to
initialize local variable when declaring them.
var
A1, A2: TTest = nil; //Set every time the code in
entered
try
A1 := TTest.Create;
---
finally
A1.Free;
end;
Comment by KMorwath on January 29, 09:17
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I use #1, I don't like #2 - adds a brain flip that detracts from
understanding.
TBH - I hadn't realized calling free on a nil was OK - my bad. So I'll
be adopting 3 from here on in.
Comment by Jason Chapman on January 29, 15:04
Try-Finally Blocks for Protecting Multiple Resources in Delphi
#3 if three or more create, otherwise #1.
But how often does #3 happen at the same time you
feel pleased with your code?
/k
Comment by Konrad on January 30, 07:33
Try-Finally Blocks for Protecting Multiple Resources in Delphi
My Pick is Interface based "guardians".
type
IObjectGuard = interface(IInterface)
..
/// <summary>Object Guard.</summary>
function _gO(Obj: TObject): IObjectGuard; overload;
/// <summary>Object Guard.</summary>
function _gO(Obj: TObject; out Ref): IObjectGuard; overload;
Use an IObjectGuard instance to automatically finalize objects
added to it, when it goes out of scope.
Use it as a guard against memory and resource leaks.
Create and use a guard like this:
var
sd: TSaveDialog;
begin
_gO(TSaveDialog.Create(nil), sd);
var
l: TStrings;
begin
_gO(TStringList.Create, l);
var sl: TStrings;
begin
if not btGo.Enabled then
Exit;
_gO(TStringList.Create, sl);
//:PRC, :REPORT, :CURSOR, :DESCRIBE
dmMain.odsReport.SetVariable(':REPORT', 'rpt_porz');
_gActive(dmMain.odsReport);
if DataSetRowToStrings(dmMain.odsReport, sl) > 0 then
TfmMemo.Execute('Данные порции', sl);
end;
No need to call NewObject.Free :-), no need for try-finally blocks.
For high perfomance code we can use optimized object guard
implementation.
Use an array instead of e.g. a TObjectList in internal list. This
reduces memory allocation overhead (we never shrink the array,
contrary to a TObjectList that shrinks when .Clear is called) and
we bypass the notification overhead of TList (see TList.Notify). }
Comment by Alexey Gavrilov on January 30, 07:33
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I remember the article from Nick about not using FreeAndNil
construction (http://www.nickhodges.com/post/Using-
FreeAndNil.aspx). Here is the analogical situation: the cleanest code
(at least to read) is by using the #1 method.
Comment by Jacek
[http://www.oeggmbh.com]
on January 30, 07:50
Try-Finally Blocks for Protecting Multiple Resources in Delphi
How about this approach:
var
A1,A2: TTest;
begin
try
A1 := TTest.Create;
A2 := TTest.Create;
A1.DoSomething;
A2.DoSomething;
finally
if Assigned(A2) then
FreeAndNil(A2);
if Assigned(A1) then
FreeAndNil(A1);
end;
end;
Comment by David Reed
[]
on January 30, 16:47
Try-Finally Blocks for Protecting Multiple Resources in Delphi
David,
that is significantly incorrect. In case A1 construction fails, A2 will
be undefined (local vars are not initialized to zero) and the Assigned
test will most likley return true even if there is only rubbish in the A2
reference. Also, Free already checks for not-nil status, so the
Assigned test is redundant anyway. And setting to nil (with
FreeAndNil) is not useful.
Comment by Marco Cantu
[http://www.marcocantu.com]
on January 31, 08:08
Try-Finally Blocks for Protecting Multiple Resources in Delphi
The only correct solution is actually #1 since in all
other cases there might be a leak if the destruction
of object A2 fails in the destructor. And we don't
wanna have leaking
resources ;)
Comment by Mike on January 31, 09:16
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I generally use #1, but have sometimes used #2 too.
#3 looks nice and symmetric, but #2 has no redundant
code, while #3 has. But I would not rewrite #3 if
someone used it.
Comment by Rudy Velthuis
[http://rvelthuis.de]
on January 31, 12:51
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I'm with Mike: as unlikely as it might be, there is
still the possibility that an exception can slip in
(unintentionally) when an object is free'd, thus
invalidating scenarios 2 and 3.
I've seen Alexey's suggested method before; that
might be a "cleaner" way out of it
Comment by Dave Nottage
[http://www.delphiworlds.com]
on February 1, 07:04
Try-Finally Blocks for Protecting Multiple Resources in Delphi
" is an error happen during the constructor
execution, Delphi will automatically execute the
destructor for the partially initialized object"
So "A1 := TTest.Create;" is outside of the try finally
block, but A1.Free will still be executed? Where can
I find more info about this?
Comment by Karoly Horvath on February 5, 05:36
Try-Finally Blocks for Protecting Multiple Resources in Delphi
Karoly,
that is correct. Partially created object gets their destructor invoked,
which is why you should never assume in a constructor that all sub-
objects are have been created and all resources have bee allocated. I
found a blog post from years ago, but it was linking to an external
resources now gone. I cover this in my Object Pascal book, I
believe...
Comment by Marco Cantu
[http://www.marcocantu.com]
on February 5, 09:28
Try-Finally Blocks for Protecting Multiple Resources in Delphi
Mike and Dave Nottage,
You've raised a concern that with option #3 if an exception is raised
in A2's destructor then A1 will leak. However, I must point out that if
any destructor fails, you're leaking already. And your application can
be expected to be flaky from then on.
Regards,
Comment by Craig Young on February 5, 13:38
Try-Finally Blocks for Protecting Multiple Resources in Delphi
Hi Marco,
I'm fine with either #1 or #3. It's important that developers
understand both.
I'm not keen on the way #2 mixes paradigms though.
My preferred option would require new syntax. :)
using
A1 := TTest.Create;
A2 := TTest.Create;
do
A1.DoSomething();
A2.DoSomething();
end;
{ and the compiler handling resource protection automatically
according to the declared types. }
Comment by Craig Young on February 5, 13:43
Try-Finally Blocks for Protecting Multiple Resources in Delphi
#3 is the best choice. Easiest to understand for all experience levels.
Comment by Dean on February 5, 20:03
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I stand with Alexey Gavrilov, much easier.
And Alexey, JCL already implements interface-based
guardians.
Comment by Fabricio Araujo on February 26, 19:12
Try-Finally Blocks for Protecting Multiple Resources in Delphi
I don't quite understand the problem with 'How NOT to write the
Code', where you say 'The issue with this code is that in case the
creation of A2 fails'. Well if A2 failed, we get an exception won't
we? And the code jumps hell knows where and the user will
scream 'SUPPORT'. So fix the godamn problem, who cares A1
leaks at that point? User is going to be annoyed by the exception
and the fact application doesn't work, not because A1 leaked.
I like Alexey's approach and probably I'll start to use it. Until now
my approach was the BAD one, I knew about the leak ... but really,
couldn't care less about that. if there was an AV to fix.
Comment by Lucian on September 29, 23:43
Try-Finally Blocks for Protecting Multiple Resources in Delphi
Something interesting to not in solutions #2 and #3... If you look
closely you will notice the resources are disposed in reverse order. I
remember in the good ol' days of Turbo Pascal, one would that to
reduce memory frag. Is this still the case? Any other reasons?
Comment by Lucian on September 29, 23:51
Post Your Comment
Click
here for posting
your feedback to this blog.
There are currently 0 pending (unapproved) messages.