Unit finalization order for application, compiled with run-time packages?

老子叫甜甜 提交于 2019-12-04 05:30:47

Units are finalized in reverse order of initialization. The order of initialization is determined by a non-cyclic (i.e. never descends into an already-visited unit) post-order traversal of the unit uses graph, starting with the main uses clause (in the program or library). SysInit is normally the first unit to be initialized, followed by System.

Dynamic loading of packages complicates things, because the main EXE or DLL gets to specify the order of initialization of the units used by the main image. So when a package is dynamically loaded, it will run what it thinks should be the initialization order, but units already initialized will be skipped; when the package is dynamically unloaded, this happens in reverse.

The general rules:

  • lower-level things should be initialized before higher-level things
  • finalization should be in reverse order of initialization

These rules almost always make sense. Higher-level units' initializations often rely on services provided by lower-level units. For example, without SysUtils, there is no exception support in Delphi. Reverse order finalization makes sense for the same reason: high-level finalizations rely on services provided by lower-level units, so they must run before the lower-level units' finalizations.

All that said, with respect to your problem, it sounds like there may be a bug somewhere in the compiler or RTL, if what you say is true: that the main EXE uses MyUnit first, and MyUnit uses no other units in its interface or implementation, and there's no funny business going on with dynamically loaded packages. All I can suggest is to keep paring down the project with the odd behaviour until you have a minimal reproducing sample; at that point, it should be clear exactly what is causing the problem.

I was able to find a reason and I feel myself a bit stupid now :)

My second test application have a static reference to DLL, which was compiled with RTL.bpl (it's empty, except for references to SysUtils and having 1 simple routine). So, since DLL is statically linked, it is initialized before any code from exe have chances to run.

That's it:

DLL's System -> DLL's SysUtils -> exe's System (skipped) -> MyUnit -> exe's SysUtils (skipped) -> etc

Finalizations are in reverse order, leading to execution of MyUnit before SysUtils.

Solution: require to include MyUnit first in all projects.

(oh, how I wish to have a time-machine to travel back in time and force somebody to add OnBeforeMMShutdown event :D )

You're not missing anything. That's exactly what's happening.

When your program loads a package, it will initialize all the units used in that package. When it unloads the package, it has to finalize all the units. But finalization often involves freeing variables. Remember that double-frees are a Bad Thing, especially if they happen during finalization when certain exception-handling features might have been unloaded, making debugging very difficult. So it puts a reference counter on the unit initializations so that they don't get finalized until everything that was using them is done with them.

Is there any particular reason why your MyUnit needs to finalize after SysUtils?

I am not sure but isn't there still the good old ExitProc global variable of Turbo/BorlandPascal fame? If yes, this could solve your problem.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!