C++ Order Of Destruction

Started by
10 comments, last by Evil Steve 16 years, 9 months ago
Me again [smile] As some of you might know, I'm trying to write a memory manager for my engine. However, as I found last night, the STL keeps hold of some memory that is only freed when a global variable inside a STL source file is destroyed. Now, I know that the order of construction / destruction isn't defined for global variables, and it appears that atexit() handlers fire before globals are destroyed. What I'd like to know is, is there any way I can have a function called after globals have been destroyed? For instance, at what point are DLLs unloaded? I could put something in a DLL if it'll work. There was an article I read somewhere that covered the order things are created and destroyed (The article was about writing your own loader I think), but I can't find it now. Platform specific answers are perfectly fine (Win32), I expect I'll need to do some crazy stuff to get this to work at all. Cheers, Steve
Advertisement
I don't have a solution for you, but I'm interested in knowing which part of the STL is holding that memory and when it got allocated. Could you tell a bit more about this issue?
Simulation & Visualization Software
GPU Computing
www.organicvectory.com
Quote:Original post by El Greco
I don't have a solution for you, but I'm interested in knowing which part of the STL is holding that memory and when it got allocated. Could you tell a bit more about this issue?
Sure, I made a post about this last night in My GDNet Journal.
IIRC theres a gcc extension which lets you specify the priority of globals to control their construction order. That should let you do what you want I think.

Obviously this is non-portable though.

I've previously solved initialisation order problems with 'globals' being static within methods (eg. Obj& getObj() { static Obj o; return o; } ), however I have no idea if the destruction order of these objects is well defined or not.
Quote:Original post by OrangyTang
IIRC theres a gcc extension which lets you specify the priority of globals to control their construction order. That should let you do what you want I think.

Obviously this is non-portable though.

I've previously solved initialisation order problems with 'globals' being static within methods (eg. Obj& getObj() { static Obj o; return o; } ), however I have no idea if the destruction order of these objects is well defined or not.
Hmm, any idea if there's a MSVC version of that? I'll dig into the MSDN.

The globals being static in methods half works - but that causes the static to be created after the globals, meaning it's destroyed before them, which doesn't help.

The global in question is in STL code; I can't change that.


The first thing sounds exactly what I want, though.
Do you have vs/vc++2k5 SP1 installed? the reason i ask is vc++2k5's string streams have memory leaks and the SP1 sorts it out as well as many other issues of course.

EDIT: There is another SP (which you install after SP1) if you're runing vs/vc++2k5 on Vista.
Quote:Original post by snk_kid
Do you have vs/vc++2k5 SP1 installed? the reason i ask is vc++2k5's string streams have memory leaks and the SP1 sorts it out as well as many other issues of course.

EDIT: There is another SP (which you install after SP1) if you're runing vs/vc++2k5 on Vista.
Nope, I don't have SP1 installed. For some reason I thought that was only for Express.

Just checked my code at work (VC2005 Express, SP1) and it has the same "leaks". These aren;t really leaks, because they do get freed up. Just after my memory manager...

I'm not running Vista on my dev machine(s), so that's not an issue.
I'll poke around in your journal for the full story, but for what it's worth the way my old C++ memory tools worked was set up in such a way as to not track the memory allocated by the SC++L or other external tools, at least not explicitly; I only tracked allocations from code I actually wrote (which might include custom allocators to the SC++L objects, though).

I never ran into the problem you're describing, iirc. Although this was years ago, so perhaps the newer SC++L implementation shipping with modern compilers would exhibit the issue now.

EDIT: Okay. It's the call to new to allocate the facet (a facet is a term for an interface to a service provided by the locale library, for what it's worth) that is trapped by your manager and then reported as a leak, right?

The traditional, simple way to avoid this issue is to provide headers to enable/disable the #define macros you use to redirect new and delete (you do use macros for this, right?). It becomes a bit obnoxious to wrap your SC++L includes, or order them appropriately so they come into the translation unit before your macro, but it works. There are a few more way, possibly slightly cleaner, but they depend somewhat on the actual implementation detail of the system used to trap calls to new. Can you provide your code?
Quote:Original post by jpetrie
I'll poke around in your journal for the full story, but for what it's worth the way my old C++ memory tools worked was set up in such a way as to not track the memory allocated by the SC++L or other external tools, at least not explicitly; I only tracked allocations from code I actually wrote (which might include custom allocators to the SC++L objects, though).

I never ran into the problem you're describing, iirc. Although this was years ago, so perhaps the newer SC++L implementation shipping with modern compilers would exhibit the issue now.
Yeah, I chose to track STL allocations just so I'm catching everything.


Anyway, I've solved it. Thanks to mattd (See, TA can be useful sometimes [smile])
#pragma init_seg is what I needed. Adding this code forces my memory manager to be created before and destroyed after STL globals:
#pragma warning(disable:4074) // warning C4074: initializers put in compiler reserved initialization area#pragma init_seg(compiler)struct MemoryInitialiser{	MemoryInitialiser() { PMemory::Create(); }	~MemoryInitialiser() { PMemory::Destroy(); }} g_theMemoryInitialiser;


Thanks to all who helped [smile]
Here are a few more solutions if anyone wants to stay away from compiler specific intrinsics.

1) Have the memory manager as a global object at the top of your main application. Obj files that are for the executable get initialized before library statics. Also, the the order of globals, within a compilation unit, are initialized from top down. If your memory manager is the first global variable in that file and doesn't depend, contain or inherit from any other class then you'll have it being initialized first.

2) The most portable way (I define portable being this has worked for me on the most platforms and most compilers) is to re-write the CRT initialization and shut down code. Have your own function get called instead of the CRT start up. You can do this through the linker option /ENTRY in visual studio. It's not too hard and most of the time you can copy and paste the compilers CRT code, remove the stuff you don't need, and stick your memory manager initialization before the global variables are constructed.

-= Dave
Graphics Programmer - Ready At Dawn Studios

This topic is closed to new replies.

Advertisement