6
votes

From std::set_new_handler

The new-handler function is the function called by allocation functions whenever a memory allocation attempt fails. Its intended purpose is one of three things:

  • make more memory available
  • terminate the program (e.g. by calling std::terminate)
  • throw exception of type std::bad_alloc or derived from std::bad_alloc

Will the following overload gurantees anything ?

void * operator new(std::size_t size) throw(std::bad_alloc){
    while(true) {
        void* pMem = malloc(size);
        if(pMem)
            return pMem;

        std::new_handler Handler = std::set_new_handler(0);
        std::set_new_handler(Handler);

        if(Handler)
            (*Handler)();
        else
            throw bad_alloc();
    }
}
2
What is your question? What do you mean by "guarantees anything"?Jonathan Wakely
@JonathanWakely, I shouldn't have used the "guarantee" word. What I meant is: can that chances of successful allocation increases by any means using this overloaded method ?P0W
@n.m. "magic" in what context ?P0W
The posted implementation will invoke the current new-handler (if one is set) so the new-handler could try to free some memory and throw std::bad_alloc and the caller could catch it and re-try the new call. How (and if) that works is completely application-specific, and only needed in quite special cases. In most apps either don't worry about allocation failure or catch the bad_alloc and exit gracefully.Jonathan Wakely
Sorry I have misread the question. This overload is a more or less faithful implementation of the standard operator new, and as such, it doesn't guarantee anything beyond what the standard operator new guarantees.n. 1.8e9-where's-my-share m.

2 Answers

16
votes

std::set_new_handler doesn't make memory available, it sets a new-handler function to be used when allocation fails.

A user-defined new-handler function might be able to make more memory available, e.g. by clearing an in-memory cache, or destroying some objects that are no longer needed. The default new-handler does not do this, it's a null pointer, so failure to allocate memory just throws an exception, because the standard library cannot know what objects in your program might not be needed any more. If you write your own new handler you might be able to return some memory to the system based on your knowledge of the program and its requirements.

8
votes

Here is a working example illustrating the functioning of custom new handlers.

#include <iostream>
#include <new>

/// buffer to be allocated after custom new handler has been installed
char* g_pSafetyBuffer = NULL;

/// exceptional one time release of a global reserve
void my_new_handler()
{
    if (g_pSafetyBuffer) {
        delete [] g_pSafetyBuffer;
        g_pSafetyBuffer = NULL;
        std::cout << "[Free some pre-allocated memory]";
        return;
    }
    std::cout << "[No memory to free, throw bad_alloc]";
    throw std::bad_alloc();
}

/// illustrates how a custom new handler may work
int main()
{
    enum { MEM_CHUNK_SIZE = 1000*1000 }; // adjust according to your system
    std::set_new_handler(my_new_handler);
    g_pSafetyBuffer = new char[801*MEM_CHUNK_SIZE];
    try {
        while (true) {
            std::cout << "Trying another new... ";
            new char[200*MEM_CHUNK_SIZE];
            std::cout << " ...succeeded.\n";
        }
    } catch (const std::bad_alloc& e) {
        std::cout << " ...failed.\n";
    }
    return 0;
}

I do not suggest the demonstrated strategy for production code, it may be too heavy to predict, how many allocations will succeed after your new_handler is called once. I observed some successful allocations on my system (play with the numbers to see what happens on yours). Here's one possible output:

Trying another new...  ...succeeded.
Trying another new...  ...succeeded.
Trying another new...  ...succeeded.
Trying another new...  ...succeeded.
Trying another new...  ...succeeded.
Trying another new... [Free some pre-allocated memory] ...succeeded.
Trying another new...  ...succeeded.
Trying another new...  ...succeeded.
Trying another new...  ...succeeded.
Trying another new... [No memory to free, throw bad_alloc] ...failed.

Process returned 0 (0x0)   execution time : 0.046 s
Press any key to continue.

Instead, from my perspective, do the release of a safety buffer only for terminating your program in a safe way. Even proper exception handling needs memory, if there isn't enough available, abort() is called (as I learned recently).