6

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();
    }
}
P0W
  • 46,614
  • 9
  • 72
  • 119
  • 1
    What is your question? What do you mean by "guarantees anything"? – Jonathan Wakely Sep 22 '13 at 15:41
  • @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 Sep 22 '13 at 16:04
  • @n.m. _"magic"_ in what context ? – P0W Sep 22 '13 at 16:06
  • 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 Sep 22 '13 at 18:21
  • 1
    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. m. could be an AI Sep 22 '13 at 21:55

2 Answers2

17

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.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Thanks. Is there's any example/reference to write new-handler that can clear in-memory cache ? – P0W Sep 22 '13 at 16:12
  • No, it depends on your application. If you don't even _have_ such a cache then it's not possible. If you have a global cache of objects then make your new-handler remove old elements, but the details would be completely specific to your application. It should be easy though - just expire things from the cache. If you need help doing that then you probably don't need to use a new-handler anyway. – Jonathan Wakely Sep 22 '13 at 18:15
9

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).

Community
  • 1
  • 1
Wolf
  • 9,679
  • 7
  • 62
  • 108