C ++: ошибка обработки ошибок в потоках - PullRequest
3 голосов
/ 19 января 2009

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

Обычно окно имеет собственный поток, и устройство 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, который является другим потоком.

Ответы [ 5 ]

4 голосов
/ 19 января 2009

Возможно, вы можете заключить вызов в другую функцию, используя 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();
}
2 голосов
/ 19 января 2009

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

Наконец, использование синхронного 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;
}
1 голос
/ 19 января 2009

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

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

0 голосов
/ 14 января 2010
0 голосов
/ 19 января 2009

Ну, ответ в том, что вы не можете. Жизнеспособной альтернативой будет выполнение только необходимой работы, которая не может завершиться сбоем, например извлечение значения из параметров, в функции обратного вызова и вызов функции 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...