C ++: проблема обработки исключений между потоками с boost :: exception - PullRequest
4 голосов
/ 20 марта 2009

По сути, у меня есть ситуация, когда один поток генерирует исключение, которое должен обработать другой поток. Я пытаюсь сделать это с исключением boost, однако где-то вдоль линии исключение теряет свой тип и, следовательно, не перехватывается блоками catch.

По сути, поток B хочет что-то сделать, однако по разным причинам это должно быть сделано с потоком A (если вы хотите узнать эти причины, спросите MS, почему устройство direct3d 9 должно создаваться, сбрасываться и освобождаться тем же потоком, который создал окно). Если во время выполнения этих действий возникает исключение, поток A перехватывает его, передает его обратно в поток B, который затем перебрасывает его для обработки по мере необходимости. Проблема в том, что исключение, выданное в потоке B, похоже, отличается от исключения, выданного в потоке A.: (

Вывод отладочной программы из моей программы и код ниже.

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...");
}

Ответы [ 4 ]

2 голосов
/ 20 марта 2009

Убедитесь, что каждый бросок из потока A использует оболочку boost :: enable_current_exception. Смотрите это .

"Если не включено enable_current_exception в то время вызывается объект исключения используется в выражении броска, попытаться скопировать его с помощью current_exception может вернуть exception_ptr, который ссылается на экземпляр unknown_exception. "

Для этого вам может понадобиться перехватить все исключения в потоке A и перебросить их после переноса с помощью throw boost :: enable_current_exception (your_exception).

Кроме того, это не будет работать для структурированных исключений, таких как деление на ноль, если вы не используете _set_se_translator для вызова оболочки и обертывания их в объекте исключения.

1 голос
/ 14 января 2010
  1. Сгенерировать исключение с помощью BOOST_THROW_EXCEPTION.

  2. В потоке catch (...), а затем вызову boost :: current_exception для помещения исключения (каким бы оно ни было) в boost :: exception_ptr.

  3. Получить исключение_ptr, скопированное в основной поток, затем вызвать rethrow_exception. Это перезапустит объект исключения, который catch (...) перехватил в потоке.

См. Www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html.

1 голос
/ 21 марта 2009

Я нашел решение для своих собственных типов исключений.

Baiscly Я добавил 2 виртуальных метода ко всем моим типам исключений.

  • виртуальная база * Clone (); клонирует исключение и возвращает новое в куче. Это обходит тот факт, что каждый из них уничтожается при выходе из блока улова.
  • virtual void ThrowAndDelete (); это делает локальную копию объекта исключения, выделенного для кучи, удаляет объект кучи (для предотвращения утечек памяти) и затем выбрасывает копию стека. Это имеет преимущество броска как наиболее производного типа.

Это означает, что теперь я могу просто сделать:

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
    {
        ...
    }
}

Я не уверен, что делать с исключениями std :: и boost :: varity, так как они, похоже, не имеют эффекта вышеупомянутого ... однако 95% + исключений, которые, вероятно, не являются передо мной будут работать мои собственные классы, и те, которые не будут, почти наверняка останутся необработанными ...

0 голосов
/ 20 марта 2009

Я думаю, что у вас есть проблема, потому что вы не выбросили исключение с помощью метода Boost.

вы можете перехватить конкретное исключение в потоке A в подключаемой процедуре, скопировать его в новый объект, вернуть указатель ..
проверьте результат в SendCallback и бросьте, если результат существует (не NULL).
но вы должны быть уверены, что NULL будет возвращаться всегда, если у вас нет исключений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...