On 11.04.2016 15:27, Maciej Izak wrote: > 2016-04-11 14:38 GMT+02:00 Sven Barth <pascaldra...@googlemail.com > <mailto:pascaldra...@googlemail.com>>: > > There is another problem that occurred to.me <http://to.me> after my > previous mail. Have you ever heard of the "static initialization > order fiasco"? I've stumbled upon that a few weeks ago at work with > C++ and was glad that we don't have that problem in Pascal, but with > the new management operators and the need to have global variables > be initialized correctly as well this would change - which would be > very bad as this is a hard to debug problem. > > Even worse, currently is possible to achieve some similar... I've > experienced from another side - "static finalization order fiasco" for > Generics.Collections (fixed 24 Dec 2015 > https://github.com/dathox/generics.collections/commit/fda586932bd80ef58c08f8ebf5a24316ca4ccca5 > ). > > I can confirm that "static initialization/finalization order fiasco" > bugs are really hard to debug. In my case for Generics.Collections was > something like this (maybe not the same but very similar): > > === X === > program X; > > uses > UnitA, ..., UnitZ, GC, Generics.Collections, Unit1; > ... > === GC === > ... > interface > var > G: Contnrs.TObjectList; > implementation > ... > initialization > G := Contnrs.TObjectList.Create(True); > finalization > G.Free; // !!! > end. > > === Unit1 === > ... somewhere in code ... > var > d: TDictionary<string, string>; > begin > d := TDictionary<string, string>.Create; > G.Add(d); > === END CODE === > > at line marked by "!!!" was memory corruption, because any of manual > interface used by dictionary was already destroyed in static/class > destructor in Generics.Defaults... Really hard to find that kind of bug. > What was funny the corruption was visible only on android... Anyway bug > is fixed :)
My example would be this: === unit1 begin === unit unit1; interface function GetSomething: PSomeType; implementation var gSomething: TSomeType; { assume TSomeType is a record that contains an initializer method that simply zeros the record and it also has a TObject field called "inst" } function GetSomething: PSomeType; begin if not Assigned(gSomething.inst) then gSomething.inst := TObject.Create; Result := @gSomething; end; { gSomething will implicitely be initialized here } end. === unit1 end === === unit2 begin === unit unit2; interface implementation uses unit1; initialization Writeln(GetSomething^.inst.ToString); end. === unit2 end === Now depending on the order of the execution of the two initialization sections we'll either have correct behavior (unit1 before unit2) or we'll have a memory leak (unit2 before unit1), because gSomething's initializer in unit1 will fill the instance variable that got assigned during unit2's initialization (the GetSomething() call) with zeros. In a simple program the order can be controlled easily, but in a more complex scenario (think the compiler itself or Lazarus) this might no longer be the case and it's essentially random for the user. I know this is a rather constructed example, but it's similar to the C++ code we had at work, so it's code that can happen in the real world. If we don't find a way to solve this problem then I'm afraid that I won't include your changes in trunk, cause I don't want to open that can of worms. Regards, Sven _______________________________________________ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel