C++: проблема обработки ошибок в потоках

В общем, я использую исключения для работы с ошибками, однако проблема, с которой я столкнулся, заключается в том, что ошибка относится к другому потоку, чем тот, который ее вызвал.

В основном у окна есть собственный поток, и устройство direct3d должно быть создано и сброшено тем же потоком, который создал окно. Однако создание устройства может завершиться неудачно, поэтому мне нужно вызвать исключение в потоке, который пытался создать экземпляр, а не в коде окна.

Функция обратного вызова:

void Callback(HWND hwnd, boost::function<void(HWND,LPARAM)> call, LPARAM lParam)
{
    //Make our stack allocated function object into a heap allocated one
    boost::function<void(HWND,LPARAM)> *callH = new boost::function<void(HWND,LPARAM)>(call);
    //send a message with a pointer to our function object in the WPARAM
    PostMessage(hwnd, WM_CALLBACK, (unsigned)callH, lParam);
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if (msg == WM_CALLBACK)
    {
        //retreive the function pointer from the WPARAM
        boost::function<void(HWND,LPARAM)> *callH = (boost::function<void(HWND,LPARAM)>*)wParam;
        //call it
        (*callH)(hwnd,lParam);
        //delete our heap allocated function object
        delete callH;
        return 0;
    }
    else
        //if there was nothing relevant to us, call the old message procedure
        return CallWindowProc(hooked[hwnd], hwnd, msg, wParam, lParam);
}
//std::map<HWND, WNDPROC> hooked;

Код, который запрашивает оконный поток для создания устройства.

Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
    window->AddRef();
    HWND hwnd = *((HWND*)window->GetHandle());
    CallbackHook(hwnd);
    Callback(hwnd, boost::bind(&Graphics::create, this), 0);

    while(!started)Sleep(100);
}
void Graphics::create()
{
...code that may throw various exceptions
    started = true;
}

Поэтому в основном мне нужно, чтобы исключения, генерируемые методом create (), перехватывались обработчиками исключений, где был создан объект Graphics, который является другим потоком.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
1 981
5

Ответы 5

Что ж, ответ в том, что вы не можете. Жизнеспособной альтернативой было бы выполнение только необходимой работы, которая не может потерпеть неудачу, например, извлечения значения g из параметров в вашей функции обратного вызова и вызова функции create () в вашем основном потоке.

Кроме того, вам не нужно ждать создания, использовать семафор, мьютекс и событие и т. д.

Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
    window->AddRef();
    HWND hwnd = *((HWND*)window->GetHandle());
    semaphore = CreateSemaphore(NULL, 0, 1, NULL);
    if (semaphore == NULL) throw whatever;
    CallbackHook(hwnd);
    Callback(hwnd, boost::bind(&Graphics::create, this), 0);

    WaitForSingleObject(semaphore);
    create();

    CloseHandle(semaphore); /* you won't need it */
}
void Graphics::create()
{
...code that may throw various exceptions
}
void Graphics::create_callback()
{
    ReleaseSemaphore(semaphore, 1, NULL);
}

Это невозможно, создание повторного запуска устройства Direct3D ДОЛЖНО выполняться с тем же потоком, который создал окно, и в 99% случаев это те, которые терпят неудачу.

Fire Lancer 19.01.2009 12:34

Ах так. Вы должны сохранить свое исключение в какой-либо переменной и затем проверить переменную в другом потоке. Посмотреть ответ далле

jpalecek 19.01.2009 13:27

Простым решением было бы перехватить исключение в потоке, который его вызвал (или проверить параметры перед вызовом функции, чтобы решить, будет ли выбрано исключение), а затем передать исключение через статическую переменную в основной поток, где основной поток может бросить.

Я бы добавил проверку после WaitForSingleObject, чтобы определить, ожидает ли исключение, чтобы быть сгенерировано, и в случае его возникновения - выбросить.

Я вижу, вы вызываете PostMessage из Callback, а затем активно ждете, используя петлю и Sleep. Замените PostMessage на SendMessage, который будет ждать завершения обработки сообщения. Таким образом, цикл, требующий интенсивной работы процессора, исчезнет. Более того, вы можете использовать код возврата вызова SendMessage (из ветви WM_CALLBACKHookProc) для передачи информации об ошибке. Либо используйте простые числовые коды (например, ненулевое значение означает ошибку), либо передайте объекты исключений (в этом случае вам придется выделить затем в куче из вашего обработчика исключений, вы не можете передать параметр напрямую, поскольку он выделен стеком ).

Наконец, использование синхронного SendMessage вместо асинхронного PostMessage означает, что вам больше не нужно создавать промежуточный функциональный объект callH из HookProc, поскольку вместо этого вы можете передать выделенный стеком параметр call. Это приводит к гораздо более простому коду.

Например:

void Callback(HWND hwnd, boost::function<void(HWND,LPARAM)> call, LPARAM lParam)
{
    //send a message with a pointer to our function object in the WPARAM
    if (SendMessage(hwnd, WM_CALLBACK, &call, lParam))
    {
        // error handling.
    }
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if (msg == WM_CALLBACK)
    {
        //retreive the function pointer from the WPARAM
        boost::function<void(HWND,LPARAM)> *callH = (boost::function<void(HWND,LPARAM)>*)wParam;
        //call it, catching exceptions in the process.
        try 
        {
            (*callH)(hwnd,lParam);
            return 0;
        } 
        catch (...) 
        {
            return 1;
        }
    }
    else
        //if there was nothing relevant to us, call the old message procedure
        return CallWindowProc(hooked[hwnd], hwnd, msg, wParam, lParam);
}
//std::map<HWND, WNDPROC> hooked;

А также:

Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
    window->AddRef();
    HWND hwnd = *((HWND*)window->GetHandle());
    CallbackHook(hwnd);
    Callback(hwnd, boost::bind(&Graphics::create, this), 0);

    // No need to wait, as SendMessage is synchronous.
}
void Graphics::create()
{
...code that may throw various exceptions
    started = true;
}

Возможно, вы можете заключить вызов в другую функцию, используя Boost.Exception.

Следующий код не работает, но вы, вероятно, поняли идею.

class Context
{
public:
    Context(HWND hwnd, const boost::function<void(HWND,LPARAM)>& f, LPARAM lParam)
    {
        // TODO: reroute call through Wrapper function.
    }

    void wait()
    {
        mutex::scoped_lock l(m);
        while (!finished)
        {
            c.wait(l);
        }
        if (ex)
            rethrow_exception(ex);
    }

private:
    void Wrapper()
    {
        try
        {
            f(/*params*/);
        }
        catch (...)
        {
            ex = current_exception();
        }
        mutex::scoped_lock l(m);
        finished = true;
        c.notify_all();
    }

    boost::function<void(HWND,LPARAM)> f;
    exception_ptr ex;
    bool finished;
    mutex m;
    condition c;
};

void Callback(HWND hwnd, const boost::function<void(HWND,LPARAM)>& f, LPARAM lParam)
{
    Context ctx(hwnd, f, lParam);

    ctx.wait();
}

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

Ответы должны быть написаны таким образом, чтобы сделать их невосприимчивыми к гниению ссылок, т.е.они должны содержать фактические ответы, а не перенаправления.

Sebastian Mach 27.03.2013 12:32

Другие вопросы по теме