0

My memory allocator is initialized when overloaded new operator is called:

MyAllocator* GetAllocator()
{
    static MyAllocator allocator;
    return &allocator;
}

void* operator new(size_t size)
{
    return GetAllocator()->Allocate(size);
}

When allocator is constructed, it creates logger. On destructing, it prints out all memory leaks.

MyAllocator::MyAllocator()
{
    m_Logger = Logger::Create("Allocator.log");
    m_Logger->Writef("...");
}

MyAllocator::~MyAllocator()
{
    m_Logger->Writef("%u Leak(s) Total", m_LeakCount);
}

Because Logger::Writef uses local static buffer, I need static mutex.

Logger::Writef(const char* fmt, ...)
{
     static char buffer[4096];
     static Mutex mutex;

     mutex.Lock();
     ...
}

With this, we get following initialization order:

  • GetAllocator::allocator
  • Logger::Writef::mutex

Order of destruction is opposite, which gives us:

  • Logger::Writef::mutex
  • GetAllocator::allocator

And here's the deal, at point when Logger::Writef is called from destructor of MyAllocator, Logger::Writef::mutex is already destructed.

ranstar74
  • 19
  • 1
  • 3
  • 2
    And your question is now? – πάντα ῥεῖ Mar 23 '23 at 21:37
  • 2
    `Logger::Writef::mutex` is a function local static, it will live for the life time of the program. – NathanOliver Mar 23 '23 at 21:40
  • Order of destruction of static storage-duration objects is the reverse of the completion of their initialization. If one destructor depends on another you need to make sure that their order of construction is correct. – user17732522 Mar 23 '23 at 21:48
  • 1
    @NathanOliver I decompiled my project with IDA (Compiled with visual studio 2022) and compiler adds 'atexit' call for every local static variable, which destroys objects in LIFO order. atexit called first for allocator instance, then for mutex. they're destroyed in opposite order. – ranstar74 Mar 23 '23 at 21:51
  • @πάνταῥεῖ Well i'm asking how to resolve this dependency issue, I need Logger::Writef::mutex variable to be destructed only if allocator was already destructed. – ranstar74 Mar 23 '23 at 21:52
  • 2
    @ranstar74 If you call `Writef` in the constructor of `MyAllocator`, then the destruction order ought to be the other way around. The completion of the constructor calls matters, not the start of the constructor calls. – user17732522 Mar 23 '23 at 21:55
  • 1
    @ranstar74 additionally requested information [goes to the question](https://stackoverflow.com/posts/75828183/edit), not in comments please! – πάντα ῥεῖ Mar 23 '23 at 22:09

1 Answers1

0

Your problem is simple. It's caused by the order of destruction, so all you have to do is change the order of destruction.

As you noted the order of destruction is opposite to the order of construction. So you need to make sure your logger variables are constructed before your allocator.

You can do this by moving your static logger variables out of function scope and putting them at module scope. They will be constructed at program startup. By putting them inside an anonymous namespace they won't be exposed to other modules.

namespace
{
     static char buffer[4096];
     static Mutex mutex;
}

Logger::Writef(const char* fmt, ...)
{
     mutex.Lock();
     ...
}
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622