C ++:跨线程的错误处理问题

时间:2021-06-01 21:02:08

In general I use exceptions to deal with errors, however the problem I have here is that the error applies to a different thread than the one that caused it.

一般来说,我使用异常来处理错误,但是我在这里遇到的问题是错误适用于不同于导致错误的线程。

Basicly the window has its own thread, and the direct3d device must be created and reset by the same thread that created the window. However creating the device may fail, so I need to throw an exception in the thread that was trying to create the instance, not the window code

基本上,窗口有自己的线程,并且必须通过创建窗口的同一线程创建并重置direct3d设备。但是,创建设备可能会失败,因此我需要在尝试创建实例的线程中抛出异常,而不是窗口代码

The callback function:

回调函数:

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;

The code that requests the window thread to create the device

请求窗口线程创建设备的代码

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;
}

So basically I need the exceptions being thrown by the create() method to be caught by the exception handelers where the Graphics object was created which is another thread.

所以基本上我需要create()方法抛出的异常被创建Graphics对象的异常handelers捕获,这是另一个线程。

5 个解决方案

#1


Perhaps you can wrap the call inside another function, using Boost.Exception.

也许你可以使用Boost.Exception将调用包装在另一个函数中。

The following code does not work though, but you'll probably get the idea.

以下代码不起作用,但您可能会得到这个想法。

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


I see you call PostMessage from Callback then actively wait using a loop and Sleep. Replace PostMessage by SendMessage, which will wait for the message processing to complete. That way the processor-intensive loop is gone. Moreover, you can use the return code of the SendMessage call (from the WM_CALLBACK arm of HookProc) to pass error information. Either use simple numeric codes (e.g. non-zero means error) or pass exception objects (in this case you'll have to allocate then on the heap from your exception handler, you can't pass the parameter directly as it is stack-allocated).

我看到你从Callback调用PostMessage然后主动等待使用循环和Sleep。用SendMessage替换PostMessage,它将等待消息处理完成。这样,处理器密集型循环就消失了。此外,您可以使用SendMessage调用的返回代码(来自HookProc的WM_CALLBACK部分)来传递错误信息。使用简单的数字代码(例如非零意味着错误)或传递异常对象(在这种情况下,你必须在异常处理程序的堆上分配,你不能直接传递参数,因为它是堆栈分配的)。

Last, using the synchronous SendMessage instead of the async PostMessage means you no longer have to build an intermediary callH function object from the HookProc, as you can pass the stack allocated call parameter instead. This results in much simpler code.

最后,使用同步SendMessage而不是async PostMessage意味着您不再需要从HookProc构建中间callH函数对象,因为您可以传递堆栈分配的调用参数。这导致更简单的代码。

For example:

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;

And:

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;
}

#3


A simple solution would be to catch the exception in the thread that thrown it (or check the parameters before calling the function to decide if an exception would be thrown) and then pass the exception via a static variable tot the main thread where the main thread can throw it.

一个简单的解决方案是在抛出它的线程中捕获异常(或在调用函数之前检查参数以决定是否抛出异常)然后通过静态变量传递异常主线程所在的主线程可以抛出它。

I would add a check after the WaitForSingleObject to discover if an exception is waiting to be thrown and in case it does - throw it.

我会在WaitForSingleObject之后添加一个检查,以发现异常是否等待抛出,以防万一 - 抛出它。

#4


Well, the answer is that you can't. A viable alternative would be to do only necessary work that can't fail, like extractiong value from the parameters, in your callback function, and call the create() function in your main thread.

那么,答案是你不能。一个可行的替代方案是只进行不会失败的必要工作,比如从参数中提取值,在回调函数中,并在主线程中调用create()函数。

Also, you shouldn't busy wait for the creation, use a Semaphore, or a Mutex & Event etc.

此外,您不应该忙等待创建,使用信号量,或互斥和事件等。

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

#1


Perhaps you can wrap the call inside another function, using Boost.Exception.

也许你可以使用Boost.Exception将调用包装在另一个函数中。

The following code does not work though, but you'll probably get the idea.

以下代码不起作用,但您可能会得到这个想法。

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


I see you call PostMessage from Callback then actively wait using a loop and Sleep. Replace PostMessage by SendMessage, which will wait for the message processing to complete. That way the processor-intensive loop is gone. Moreover, you can use the return code of the SendMessage call (from the WM_CALLBACK arm of HookProc) to pass error information. Either use simple numeric codes (e.g. non-zero means error) or pass exception objects (in this case you'll have to allocate then on the heap from your exception handler, you can't pass the parameter directly as it is stack-allocated).

我看到你从Callback调用PostMessage然后主动等待使用循环和Sleep。用SendMessage替换PostMessage,它将等待消息处理完成。这样,处理器密集型循环就消失了。此外,您可以使用SendMessage调用的返回代码(来自HookProc的WM_CALLBACK部分)来传递错误信息。使用简单的数字代码(例如非零意味着错误)或传递异常对象(在这种情况下,你必须在异常处理程序的堆上分配,你不能直接传递参数,因为它是堆栈分配的)。

Last, using the synchronous SendMessage instead of the async PostMessage means you no longer have to build an intermediary callH function object from the HookProc, as you can pass the stack allocated call parameter instead. This results in much simpler code.

最后,使用同步SendMessage而不是async PostMessage意味着您不再需要从HookProc构建中间callH函数对象,因为您可以传递堆栈分配的调用参数。这导致更简单的代码。

For example:

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;

And:

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;
}

#3


A simple solution would be to catch the exception in the thread that thrown it (or check the parameters before calling the function to decide if an exception would be thrown) and then pass the exception via a static variable tot the main thread where the main thread can throw it.

一个简单的解决方案是在抛出它的线程中捕获异常(或在调用函数之前检查参数以决定是否抛出异常)然后通过静态变量传递异常主线程所在的主线程可以抛出它。

I would add a check after the WaitForSingleObject to discover if an exception is waiting to be thrown and in case it does - throw it.

我会在WaitForSingleObject之后添加一个检查,以发现异常是否等待抛出,以防万一 - 抛出它。

#4


Well, the answer is that you can't. A viable alternative would be to do only necessary work that can't fail, like extractiong value from the parameters, in your callback function, and call the create() function in your main thread.

那么,答案是你不能。一个可行的替代方案是只进行不会失败的必要工作,比如从参数中提取值,在回调函数中,并在主线程中调用create()函数。

Also, you shouldn't busy wait for the creation, use a Semaphore, or a Mutex & Event etc.

此外,您不应该忙等待创建,使用信号量,或互斥和事件等。

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

#5