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?
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 astd::bad_alloc
thrown by the new-handler is caught, andNULL
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.