C++ Logo

std-proposals

Advanced search

[std-proposals] Deliberate memory leak

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Fri, 11 Oct 2024 12:11:51 +0100
There are times when we allocate memory which we never intend to
deallocate. For example, I'm writing a program at the moment that
loads in plugins, and the plugins stay loaded until the end of the
program.

The only problem with this is that when you run a debugging tool to
detect memory leaks, you get a load of false positives. I had a
program before that linked with libpango, and I spent ages trying to
find a memory leak, wondering if I was misusing the library or if the
library internally had a bug, and I went the whole hog and joined the
libpango mailing list and shared all my debugging output. In the end
it turned out to be a benign memory leak -- i.e. the allocation
happened once in the program and was intended to last the entire
program.

But what if we could do the following:

    void *p = malloc( 1024ul * 1024ul * 64ul );
    std::deliberate_leak(p);

Or:

    long unsigned *p = new long unsigned[2048u];
    std::deliberate_leak(p);

It could work something like this: https://godbolt.org/z/Gx6Pc6ne5

And then when you compile in Release Mode, you don't bother deallocating.

And here it is copy-pasted:

#include <cstdlib> // abort, atexit, free
#include <mutex> // lock_guard, mutex

#define PRINT_TO_SCREEN // ------- You can comment this line out -------

#ifdef PRINT_TO_SCREEN
# include <cstdint> // uintptr_t
# include <iomanip> // hex
# include <iostream> // cout, endl
#endif

void DeliberateLeak(void const volatile *const arg) noexcept
{
    constexpr unsigned capacity = 64u;
    static void const volatile *leaks[capacity] = {}; // all nullptr's

    static std::mutex m;
    std::lock_guard mylock(m);

    if ( nullptr == leaks[0u] )
    {
        std::atexit(
            [](void) noexcept -> void
            {
                for ( unsigned i = 0u; i < capacity; ++i )
                {
                    if ( nullptr == leaks[i] ) return;
                    std::free( const_cast<void*>(leaks[i]) );
#ifdef PRINT_TO_SCREEN
                    std::cout << "Freeing deliberately leaked memory
at address 0x"
                              << std::hex
                              << reinterpret_cast<std::uintptr_t>(leaks[i])
                              << std::endl;
#endif
                }
            });
    }

    static unsigned index = 0u;
    if ( index >= capacity ) std::abort(); // no more free slots available
    leaks[index++] = arg;
}

int main(void)
{
    void *p1 = std::malloc(1024ul * 1024ul * 64ul);
    DeliberateLeak(p1);
    void *p2 = std::malloc(1024ul * 1024ul * 64ul);
    DeliberateLeak(p2);
}

Received on 2024-10-11 11:12:06