3

If the new operator fails to allocate memory, the exception std::bad_alloc is only getting caught if I put a try-catch block immediately surrounding the new statement. If I instead have the try-catch block in a caller a few stackframes down below, it is not getting caught there and I'm getting an abnormal program termaination. Why does this happen? This is on Microsoft Visual Studio 2008.

Edit: Ok, here is the code that is not working. The function immediately below is where I'm calling the new, and the ones below are the stackframes below it. The last function is where I have the catch clause but it doesn't get caught there.

void HTTPResponseBuff::grow()
{
    if (m_nMaxSize > m_nStartConstGrowSize)
        m_nMaxSize += m_nConstGrowSize;
    else
        m_nMaxSize = 2 * m_nMaxSize;

    char* pTmp = new char[m_nMaxSize];
    . . . 
}

void HTTPResponseBuff::write(const char* pBuf, size_t len)
{
    char* pCh;
    while (getRemainingCapacity(pCh) < len)
        grow();
    . . . 
}

size_t HTTPTransport::responseCallback(void *pRespData, size_t size,
                             size_t nmemb, void *pRespBuff)
{
    const char* pChar = (const char*)pRespData;
    register int respDataLen = size * nmemb;    
    ((HTTPResponseBuff*)pRespBuff)->write(pChar, respDataLen);
    return respDataLen;
}

A few curl library stackframes here. These are C code, not C++.

ISTATUS HTTPTransport::invoke()
{
    invokeCleanup();

    //make the HTTP call
    CURLcode retCode;
    try{
    retCode = curl_easy_perform(m_pCurl);
    }
    catch(std::bad_alloc& ba)
    {
        strcpy(m_pErrMsg,ba.what());
        m_status = IFAILURE;
    }
}

Also the stackframes at the time I caught the bad_alloc (in the catch clause immediately surrounding the new statement) is here: http://s289.photobucket.com/albums/ll211/spiderman2_photo_bucket/?action=view&current=bad_alloc.jpg

Walking Corpse
  • 107
  • 7
  • 31
  • 49
  • Which programmar language is it? It seems to be C++ oder C. – Reporter Jul 27 '11 at 09:42
  • C++. Does it have anything to do with the fact that the functions in the callstack don't have a "throws bad_alloc" defined in them? I t is not possible for me to add this delcaration, because some of the stackframes in the middle are from a 3rd party. – Walking Corpse Jul 27 '11 at 09:44
  • 4
    Could you post a code sample that exhibits this behavior ? – Sander De Dycker Jul 27 '11 at 09:49
  • 2
    Throw-specs are not commonly used in C++ and deprecated in C++0x. I would not add them in new code. bad_alloc is used for out-of-memory situations. So you might not be allowed to allocate extra memory in between the throw and the catch. This might explain why a tight try-catch works, and bigger ones fail. – mirk Jul 27 '11 at 09:55
  • Instead of me posting the code you can take a look at this picture http://s289.photobucket.com/albums/ll211/spiderman2_photo_bucket/?action=view&current=bad_alloc.jpg DirkM, nobody is allocating extra memory in betwen the throw and my catch. That would only be possible if the inbetween stackframes from libcurl catch the exception and allocate memory in the catch clause, which is highly imaginative – Walking Corpse Jul 27 '11 at 09:57

2 Answers2

2

You mentioned third party functions inbetween the exception origin and the try-catch. If these thirdparty functions are not c++(for example have c linkage, like libcurl being written in c) then the exception handling will not work as expected. I had the same issue with a project using gcc.

You need to catch all exceptions in your callbacks before they propagate through the third party layer and use error codes( or a custom mechanism) to get the information into your caller or give up on using the exceptions entirely.


As you ask what I did: I had a context object from the caller injected into the callback through a void pointer available. So I changed the context object to have ErrorStatus and ErrorMessage members and used these to propagate errors through the C-Layer.

struct Context{
    ErrorCode errorCode;
    const char* errorMessage;
    //other stuff
}

void callback(T arg, void* context){
   try{
      new(int);
   }catch(std::exception &e){
       context.errorCode=getErrorCode(e);
       context.errorMessage=e.what();
   }

}

void caller(){
    Context context;
    thirdparty_function(context);

}

If you are not worried about thread safety you also could use global variables for that.

That said, handling out of memory errors this way could be tricky, because you should avoid allocating additional memory for error messages

tokage
  • 196
  • 3
  • yes, the intermediate stackframes indeed are C, not C++. If the exception handling will not work as expected, that makes things complicated, because the problem is known in the callback function, but how can I propagate this information downward, and stop the further execution of the program? Please tell what you did in your project? The callback function has only a void return type. – Walking Corpse Jul 27 '11 at 10:19
0

Post your code if possible, without your code I have wrote a small testing and the try/catch seems to be working fine even with a few more stackframe away(the example works fine with gcc):

void foo()
{
    int* myarray= new int[1000000000];
}

void foo1()
{
     int i = 9;
     foo();
}

void foo2()
{
     int j = 9;
     foo1();
}

int main () {
  try
  {
         foo2();
  }
  catch (exception& e)
  {
    cout << "Standard exception: " << e.what() << endl;
  }
  system("pause");
  return 0;
}

The output is

Standard exception: St9bad_alloc

RoundPi
  • 5,819
  • 7
  • 49
  • 75