On 17/09/2021 11:13, Alex Peshkoff via Firebird-devel wrote:
> On 9/17/21 12:44 PM, Dimitry Sibiryakov wrote:
>> Alex Peshkoff via Firebird-devel wrote 17.09.2021 9:08:
>>> Is it about a library dynamically (dlopen, LoadLibrary) loaded or
>>> linked to the exacutable?
>>
>>   Both. The library tree is the same (at least on Linux).
>>
> 
> Tree is the same - but cleanup order (at least on linux) differs.
> First of all dynamically loaded libraries are unloaded in reverse order,
> next invoken executable dtors/atexits, and only after it - dtors of
> linked libraries.
> Check yourself if in doubt.
> 

I'm not looking at order between different libraries at the moment.

I'm creating a possible solution for more safe integration of our memory
manager cleanup with more common code, possible implemented in external
libraries that we do not have full control.

With the atexit trick, and initialize the memory manager and mutex on
demand, things improves. But it still crashed code with boost.test
integrated.

Boost.test has a global function (GF) with a static object (GFO)
inside it. So when this function is called for the first time it
initializes that object. Everything ok here.

But in other cpp files there is the tests. Tests instantiates new
objects (via static storage) and register that tests on GFO (returned
calling GF).

Each object instantiated with new operator is initialized after the
memory manager initialization. Hence, memory manager cleanup happens
after it and everything is ok.

But relation of GFO and the memory manager does not exist. GFO may be
initialized before the memory manager and is cleaned up after it. But
GFO destructor destroy the tests objects.

We may think in a much more simple case as a global
std::unique_ptr<std::string> where the string is set later after global
initialization. That smart pointer may be cleaned up after the memory
manager.

For the boost.test case a solution exist (maybe not completely
reliable). Since it have a .cpp-like file which is included in a .cpp
file of us, I have created a DefaultMemoryPoolGuard RAII class put in
our .cpp file, before inclusion of boost.test. It makes memory manager
cleanup run later.

But this is very boost.test specific and will not work in other cases.

But we have two kind of external projects. Shared libraries (say ICU)
and static libraries (say RE2).

If ICU does not cleanup its memory properly, we'll have memory leaks
after it's unloaded, as it does not use our memory manager.

RE2 global objects (if existent) are destroyed with our default memory
pool, but if it does the things I'm saying here, we'll have crashes or
undefined behavior.

I think we can improve things here.

We have our GlobalPtr, InitInstance and we may continue using them. And
we have FB_NEW which allocates memory from the global default pool.

We may split that global default pool in two, so we'll have another
default pool for external (not controlled by us) libraries. All memory
allocated directly with "operator new" (not FB_NEW) will go to external
pool.

And external pool will not be cleaned up on exit. Like we already have
with ICU memory.

Of course we didn't intend to have all external code cleaning things and
we leak that pool structures, so it may have some special things. Once
we are destructing it if there is no memory allocated from it anymore,
it must be completely freed.

Since it's a different pool, we may debug it and understand better how
the libraries we're using are doing with it.


Adriano


Firebird-Devel mailing list, web interface at 
https://lists.sourceforge.net/lists/listinfo/firebird-devel

Reply via email to