C++: проблема обработки исключений между потоками с помощью boost::exception

По сути, у меня есть ситуация, когда один поток генерирует исключение, которое должен обрабатывать другой поток. Я пытаюсь сделать это с помощью исключения 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...");
}

person Fire Lancer    schedule 20.03.2009    source источник
comment
@Neil Butterworth Почему вы удалили часть вывода отладки VS????   -  person Fire Lancer    schedule 20.03.2009
comment
Извините, я думал, что просто поправил заголовок.   -  person    schedule 20.03.2009
comment
throw ... В случае отладочного вывода, который я предоставил: throw fllib::exception::Error(LTest exception...);   -  person Fire Lancer    schedule 20.03.2009
comment
возможно, нужно попробовать бросить с помощью throw boost::enable_current_exception(my_exception()); revergestudios.com/boost-exception/   -  person bayda    schedule 20.03.2009
comment
Хорошо, за исключением того, что я не контролирую весь код, который может генерировать исключения в этом блоке... Единственный вариант - создать массивную кучу блоков перехвата для преобразования исключений, например, catch(ExceptionType &except){boost::throw_exception(кроме );}catch(AnotherException &except){...   -  person Fire Lancer    schedule 20.03.2009
comment
Или есть другой способ передать исключение, выброшенное в потоке A, в поток B?   -  person Fire Lancer    schedule 20.03.2009
comment
я не совсем понимаю, почему вы не хотите возвращать указатель на перехваченное исключение или обернутое перехваченное исключение из процедуры ловушки и не выполняете обычный бросок из SendCallback? пожалуйста, дайте комментарии, какая функция вызывает поток формы A, что из B   -  person bayda    schedule 20.03.2009
comment
Безопасно ли возвращать указатель на исключение из блока catch? Я думал, что С++ удалил объект исключения, как только код покинул блок catch, если только он не столкнулся с броском; ? В любом случае, даже если это действительно так, как это может привести к возникновению исключения того же типа в потоке B?   -  person Fire Lancer    schedule 20.03.2009
comment
это зависит от того, как было выбрано исключение. если кинуть указатель, никто кроме вас его не удалит. вы можете создать новый объект, который будет содержать информацию об исключении или скопировать его.   -  person bayda    schedule 20.03.2009
comment
Но, как я уже говорил, я не контролирую весь код, который может генерировать исключение, мне нужен способ, который может поймать и передать любое исключение... Нет ли способа клонировать исключение в... поймать, вернуть указатель к клонированному объекту и выбросить его (как исходный тип, поэтому блоки catch обрабатывают его)?   -  person Fire Lancer    schedule 20.03.2009
comment
Я могу, по крайней мере, предположить, что исключения могут быть скопированы и скопированы, я верю... по крайней мере, я предполагаю, что все std:: и boost:: могут быть?   -  person Fire Lancer    schedule 20.03.2009


Ответы (4)


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

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

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

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

person Carlos A. Ibarra    schedule 20.03.2009
comment
Для этого... как я могу сделать это без блока catch для всех возможных типов исключений? - person Fire Lancer; 20.03.2009
comment
См. www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html. - person Emil; 21.06.2012

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

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

  • виртуальная база* Clone(); клонирует исключение и возвращает новое в куче. Это позволяет обойти тот факт, что origenall уничтожается при выходе из блока catch.
  • виртуальная пустота 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:: разнообразием исключений, поскольку они, похоже, не имеют методов для эффекта вышеизложенного... однако 95%+ исключений, которые, вероятно, не будут обрабатываться перед рукой мои собственные классы, а те, что нет, почти наверняка все равно останутся без обработки...

person Fire Lancer    schedule 20.03.2009

  1. Вызовите исключение, используя BOOST_THROW_EXCEPTION.

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

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

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

person Emil    schedule 14.01.2010

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

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

person bayda    schedule 20.03.2009
comment
дело в том, что мне нужен блок catch для каждого типа исключения, что означает, по крайней мере, все std::, boost:: и все те, которые использует мой код? И тогда как мне вернуть этот указатель обратно к нужному типу, а не генерировать исключение void*, которого ничего нельзя ожидать? - person Fire Lancer; 20.03.2009
comment
хорошая практика делает все исключения производными от std::exception. - person bayda; 20.03.2009
comment
вы можете сделать следующее: some_error_info* result = 0; catch(const ThirdParty::exception& e) { result = new your_own_error_type(e); } catch( /* исключения из boost */ ) { // делаем то же самое } catch( const std::exception& e) { // делаем то же самое } catch( ... ) { result = new unknown_error(); } вернуть результат; - person bayda; 20.03.2009
comment
Но вернемся к 1000 execpt, dynamic_casts и throws... мое исключение идет std::exception -> fllib::exception::Base -> ..., я предполагаю, что и у boost тоже? Проблема в том, что 1% кода на самом деле ловит std::exception, а код, который это делает, в крайнем случае приводит к красивому сбою. - person Fire Lancer; 20.03.2009
comment
поэтому мне нужен способ вернуться к исходному типу... и я не вижу никакого способа вернуться к исходному типу, кроме использования динамического приведения для каждого возможного типа... например, if(FileNotFound *e = dynamic_cast‹FileNotFound ›(кроме))throw *e;if(следующий... - person Fire Lancer; 20.03.2009
comment
И даже тогда требуется много обслуживания, чтобы поддерживать его в актуальном состоянии... нет ли способа клонировать std::exception&, а затем выбросить его позже как наиболее производный тип. (например, скажем, что он перехватывает и клонирует исключение DllLoadError, которое в конечном итоге унаследовано от std::exception, мне нужно повторно сгенерировать как DllLoadError...) - person Fire Lancer; 20.03.2009