Monday, February 24, 2014

Packages and initialization

Packages are an approved concept and they have been working fine for over a decade now, right? Well yeah, they should be. However my coworkers and me have been bitten by them not only once. Especially since our software consists of lots of DLLs that are linked against the RTL/VCL, 3rd party and some of our own packages.

Everything alright so far you say? Actually not because of some simple fact: initialization parts of units inside of packages might not be executed when the package gets loaded but when a binary gets loaded that uses it. Not a problem? Not really unless your initialization and finalization parts are doing the correct thing.

Let's take a look at some unit that might be part of a package:

unit Foo;

interface

procedure Bar;

implementation

uses
  Contrns;

var
  list: TObjectList;

procedure Bar;
begin
  if not Assigned(list) then
    list := TObjectList.Create;
  // do something with list
end;

initialization

finalization
  list.Free;

end.

What do you think happens when you load a DLL that uses this unit given that no other module has used this unit yet? Yep, the initialization part gets executed. Since we are using lazy initialization here there is nothing to execute. Now we call Bar and the list variable gets initialized since as we know global variables are initialized with zero (or in our case nil). Then we unload the DLL and our finalization part gets called and cleans up the instance. What do you think happens when we load the DLL again and call Bar? Most likely an AV. Wait what? Yes, list has not been set to nil because remember this unit is part of a package and thus list has not been set to nil again!

There are several places in Delphi and 3rd party source I have seen that are coded like this. Our solution? Make sure every unit that causes trouble is referenced in the main application EXE to force initialization at startup and finalization at shutdown of the application.

P.S. Did anyone shout "Never use FreeAndNil" back there?!