7

I have a program that needs to propagate exceptions from any uncaught thread to the main thread, so that it may provide the user with information about why it failed and shut down safely. My InterThreadException handler seems to work but only when the thread that throws an exception is spawned directly by the main thread.

The InterTheadException handler is invoked correctly in all cases, being signaled that an exception is propagated by having an exception pointer passed to it, and the main thread receives the notification that it has received a new exception, but the call to std::rethrow_exception on the exception pointer simply fails and seemingly does nothing. I have tested throwing the exact same exception from these two different threads, and the problem seems to persist no matter what I throw.

I suspect I have a fundamental misunderstanding on how one is supposed to use exception pointers, but I'm not sure.

Here is my implementation of the InterThreadExceptionHandler.

class InterThreadExceptionHandler : NonCopyable
{
public:
    InterThreadExceptionHandler();
    ~InterThreadExceptionHandler();

    //
    // Sends an exception into this handler, informing it that 
    //this exception has been caught
    // and may be propagated to another thread
    //
    void sendException(std::exception_ptr exception);

    //
    // Freezes the calling thread until an exception has been sent,
    //then rethrows said exception
    //
    void waitForException();

private:
    std::vector<std::exception_ptr> sentExceptions; 
    std::mutex sentExceptionsMutex;

    Semaphore sentExceptionsCounter;
};


InterThreadExceptionHandler::InterThreadExceptionHandler()
{
}
InterThreadExceptionHandler::~InterThreadExceptionHandler()
{
}

void InterThreadExceptionHandler::sendException(std::exception_ptr exception)
{
    ScopedLock lock(this->sentExceptionsMutex);
    this->sentExceptions.push_back(exception);
    this->sentExceptionsCounter.give();
}

void InterThreadExceptionHandler::waitForException()
{
    this->sentExceptionsCounter.take();
    ScopedLock lock(this->sentExceptionsMutex);

    assert( this->sentExceptions.size() > 0 );

    std::exception_ptr e = this->sentExceptions[0];
    this->sentExceptions.erase(this->sentExceptions.begin());
    if (e == std::exception_ptr())
    {
        throw std::exception("Invalid exception propagated!");
    }
    std::rethrow_exception(e);
}
Shravan
  • 2,809
  • 2
  • 18
  • 39
OnePie
  • 425
  • 3
  • 10
  • 1
    possible duplicate of http://stackoverflow.com/questions/233127/how-can-i-propagate-exceptions-between-threads – engineerC Mar 05 '13 at 00:44
  • 3
    Your class works fine with GCC on GNU/Linux (after changing `count()` to `size()` and changing the call to the non-standard `std::exception` constructor and replacing `Semaphore` with `std::condition_variable`) so it sounds like an MSVC bug. It shouldn't matter whether a thread is spawned by the `main` thread or by some other thread. – Jonathan Wakely Mar 05 '13 at 00:52
  • 1
    does it work on vs2013? – Martin Ba Apr 29 '14 at 09:38
  • does it work with VS2015 or VS2017? – Sonic78 Aug 21 '19 at 20:54

1 Answers1

0

Apologies for this not being a direct answer to your question, but it may be helpful anyway.

If I detect a runtime error in a thread either by catching an exception, checking a return code, etc, I have that thread freeze the entire process. I will also have it turn off ftracing (a Linux clone of dtrace) so that any trace logs I can get are preserved for the lead up to the problem. I'm afraid I don't know what the Windows equivalent is. I can then attach a debugger and take a look around to see what's going on, maybe even correct a value and carry on execution.

It's not so useful in deployed code but it's great during development. The view one has of the problem is good because everything is there 'as is', there's been no stack unwinding, the other threads haven't had much of a chance to get anywhere, etc. That can make fault identification easier. For deployed code I might provoke a core dump (I tend to be running on something unixy) instead. Obviously that's no good if some degree of clean up is necessary.

bazza
  • 7,580
  • 15
  • 22