views:

2161

answers:

4

Basically, I've got a situation where one thread throws an exception which a different thread needs to handle. I'm trying to do this with boost exception, however somewhere along the line the exception loses its type and thus isn't caught by the catch blocks.

Basically, thread B wants to do something, however for various reasons it must be done with thread A (if you want to know those reasons ask MS why a direct3d 9 device must be created, reset and releashed by the same thread that created the window). If, while carrying out those actions, an exception occurs, thread A catches it, passes it back to thread B, which then rethrows it to be handled as needed. The problem is that the exception thrown in thread B seems to be different from the one thrown in thread A. :(

The debug output from my program, and the code are below.

First-chance exception at 0x776b42eb ...: fllib::exception::Error at memory location 0x0019e590..  
First-chance exception at 0x776b42eb ...: [rethrow] at memory location 0x00000000..  
First-chance exception at 0x776b42eb ...: boost::exception_detail::clone_impl<boost::unknown_exception> at memory location 0x0019eed4..
//thread B
...
try
{
    SendCallback(hwnd, boost::bind(&Graphics::create, this));
}
catch(fllib::exception::Error &except)//example catch block, doesnt catch example exception
{
    ...handle exception...
}

void SendCallback(HWND hwnd, boost::function<void()> call)
{
    boost::exception_ptr *except_ptr = 
        (boost::exception_ptr*)SendMessage(hwnd, WM_CALLBACK, (unsigned)&call, SEND);
    if(except_ptr)//if an exception occurred, throw it in thread B's context
    {
        boost::exception_ptr except = *except_ptr;
        delete except_ptr;
        boost::rethrow_exception(except);
    }
}
//thread A
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if(msg == WM_CALLBACK)
    {
        if(lParam == POST)
        {
            ...
        }
        else
        {
            boost::function<void()> *call = (boost::function<void()>*)wParam;
            try
            {
                (*call)();
            }
            catch(...)
            {
                return (unsigned)new boost::exception_ptr(boost::current_exception());
            }
            return 0;
        }
    }
    else
    {
        ...
    }
}

void Graphics::create()
{
...code that may throw exceptions...
eg
    throw fllib::exception::Error(L"Test Exception...");
}
A: 

Make sure every throw from Thread A uses the boost::enable_current_exception wrapper. See this.

"Unless enable_current_exception is called at the time an exception object is used in a throw-expression, an attempt to copy it using current_exception may return an exception_ptr which refers to an instance of unknown_exception."

To do this, you may need to catch all exceptions in Thread A and rethrow them after wrapping them with throw boost::enable_current_exception(your_exception).

Also, this will not work for structured exceptions like division by zero, unless you use _set_se_translator to call the wrapper and wrap them in an exception object.

Carlos A. Ibarra
"To do this..." how can I do that without a catch block for every possible exception type?
Fire Lancer
A: 

i think you have problem because didn't throw exception with boost way.

you could catch concrette exception in thread A in hook procedure, copy it into new object, return pointer..
check result in SendCallback, and do throw if result exists ( not NULL ).
but you should be sure that NULL will be returned always if you doesn't have exception.

bb
thing is I then need a catch block for every exception type, that means at least all the std::, boost:: and all the ones my code uses? And then how do I get that pointer back to the right type rather than throwing a void* exception that nothing can expected?
Fire Lancer
good practice make all exceptions derivete from std::exception.
bb
you could do nextsome_error_info* result = 0;catch( const thirdparty::exception}catch( /* excpetions from boost */ ){// do same}catch( const std::exception}return result;
bb
sorry for unformated comment =(
bb
But thats back at having 1000's execpt, dynamic_casts and throws... my exception goes std::exception -> fllib::exception::Base -> ..., i assume the boost ones do too? The problem is like 1% of the code actauly catches an std::exception, and the code that does does as a last resort as to crash nicely
Fire Lancer
so I need a way to get back to throwing the origenal type...and I cant see any way to get back to the origenal type except using dynamic cast for every possible type...eg if(FileNotFound *e = dynamic_cast<FileNotFound>(except))throw *e;if(next...
Fire Lancer
Fire Lancer
+1  A: 

I found a solution for my own exception types.

Baiscly I added 2 virtual methods to all my exception types.

  • virtual Base* Clone(); clones the exception and returns a new one on the heap. This gets around the fact that the origenall one is destroyed when leaving the catch block.
  • virtual void ThrowAndDelete(); this makes a local copy of the heap allocated exception object, deletes the heap one (to prevent memory leaks) and then throws the stack copy. This has the advantage of throwing as the most derived type.

This means I can now simply do:

void SendCallback(HWND hwnd, boost::function<void()> call)
{
    fllib::exception::Base *except_ptr = 
        (fllib::exception::Base*)SendMessage(hwnd, WM_CALLBACK, (unsigned)&call, SEND);
    if(except_ptr) except_ptr->ThrowAndDelete();
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if(msg == WM_CALLBACK)
    {
        if(lParam == POST)
        {
            ...
        }
        else
        {
            boost::function<void()> *call = (boost::function<void()>*)wParam;
            try
            {
                (*call)();
            }
            catch(fllib::exception::Base &except)
            {
                return (unsigned)except.Clone();
            }
            return 0;
        }
    }
    else
    {
        ...
    }
}

I'm not sure what to do about the std:: and boost:: varity of exceptions since they dont seem to have mtheods to the effect of the above... however 95%+ of the exceptions that are likly not to be handled before hand are my own classes, and those that are not are almost certainly going to go unhandled anyway...

Fire Lancer
A: 

For boost::exception_ptr to work correctly, you need to throw using boost::enable_current_exception:

struct foo: virtual std::exception { };

..... throw boost::enable_current_exception(foo());

Alternatively you can just use BOOST_THROW_EXCEPTION:

BOOST_THROW_EXCEPTION(foo());

Emil