Question

Before reading this question, I had never taken exception handling seriously. Now I see the necessity but still feel "Writing exception safe code is very hard".

See this example in that question's accepted answer:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

As the answer says, I can easily offer the basic guarantee by using std::unique_ptr. However when I catch a std::bad_alloc, I don't know whether it happens in the push_back or x->doSomethingThatCanThrow(), so I can't know whether t.list is still "good" or its last element is not fully prepared. Then the only choice is to discard t, show a scary message and abort, if t is essential for the entire program.

Code with strong guarantee doesn't have the problem, but “it can become costly” (this example involves a copy of the large list), and not so readable.

A possible solution may be making new wait until memory is available, removing the most annoying exception std::bad_alloc. Then 2. and 3. won't throw (provided X's construction and copy always succeed). I can just wrap 4. in a try block and deal with exceptions here (and pop_back the list). Then the function will provide nothrow guarantee, and the list will always contain good things.

The users won't care the difference between 100% CPU and 100% RAM. When they see a program hangs, they will close other programs so new finds enough memory and continues.

My question: Can this be implemented? Is there a nothrow new that waits until memory is available? Can I apply it globally (e.g. by #define new ...) so libraries before C++ was standardized can survive a temporary 100% RAM?

Was it helpful?

Solution

It's a questionable design, but you can certainly do this with a 'new-handler'. The default new-handler simply throws std::bad_alloc. If the new-handler returns, new will loop, and attempt to allocate again. It is also used by the nothrow new operator, but a std::bad_alloc thrown by the new-handler is caught, and NULL returned, in that case.

You simply need to set the new-handler to your custom void (*)() handler function. At the very least, you might want to put the process to sleep for a while - say, 1/10 sec. Again, it might not be possible for the program to continue anyway - Linux, for example, has the 'OOM killer' which can be configured by an admin.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top