3
votes

I have a MFC-C++ application where at least two threads are running "MainFrame" (=GUI thread) and a "Solver" thread.

At one point the second thread (Solver) initiate a change in the model which the GUI thread should execute by a PostMessage(...). To be safe I want to wait that message was proceeded to continue in the second thread.

By using a SendMessage(...) the Solver thread waits for the message to be executed but in this way we bypass the message queue which shouldn't be the goal.

My question: How can I properly and clean check/trigger that my message was proceed before I continue?

  1. Did we need to check every 'x' seconds the status of the main thread message queue by GetQueueStatus(...) ?
  2. Is there a way to have the main thread send back a "success event" to the other thread? And that the second one waits to have an event back?
  3. Did boost provide an easy solution for this kind of problems?
  4. Was there already a similar question which i didn't found? (sorry)

My function:

void PostSequencerMessageToGUI(ESequencerSignal signal, CSchedulerStep * step)
{
    CMainFrame *mainFrame = theApp.GetMainFrame();
    assert(mainFrame);
    if (!mainFrame)
        return;
    mainFrame->PostMessage(WM_SEQUENCER_SIGNAL, signal, reinterpret_cast<LPARAM>(step));
    // mainFrame->SendMessage(WM_SEQUENCER_SIGNAL, signal, reinterpret_cast<LPARAM>(step));

    // Message was posted, now wait for event back to continue procedure
    // ... but how? ...and when to cancel ? ...
    return; 
}
1
The bug is in your code. If it matters, whether a message is posted to the message queue, or directly passed to the window procedure, you have more sever issues than this one. At any rate, if you want something like PostMessage with a reply, use SendMessageCallback.IInspectable
Just use a threading synchronization object, like an autoreset event, the kind you create with CreateEvent. Call SetEvent() in the UI thread after it has processed the message. And WaitForSingleObject() in your worker thread. If you also need to pass back "success" then add a bool or int member to CSchedulerStep.Hans Passant
Using CreatEvent and SetEvent would probably work too.Nicolas De Vos

1 Answers

0
votes

As SendMessage is ignoring the current message queue (it pass the queue) i couldn't use this approach.

I found my solution in another question where it is solved using a Condition variables and a Mutex. https://stackoverflow.com/a/16909012/5036139

My solution:

#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
boost::condition_variable g_sequencerJobCondition;
boost::mutex g_guiMutex;

void PostSequencerMessageToGUI(ESequencerSignal signal, CSchedulerStep * step)
{
    CMainFrame *mainFrame = theApp.GetMainFrame();
    assert(mainFrame);
    if (!mainFrame)
        return;

    bool work_is_done = false;

    try {
        boost::mutex::scoped_lock lock(g_guiMutex);
        mainFrame->PostMessage(WM_SEQUENCER_SIGNAL, reinterpret_cast<WPARAM>(&work_is_done), reinterpret_cast<LPARAM>(step));

        // Message was posted, now wait for event back to continue procedure
        while (!work_is_done){
            g_sequencerJobCondition.wait(lock);
        }
    } 
    catch (... /*e*/){
        // Handle all kind of exception like boost::thread_resource_error, boost::thread_interrupted, boost::exception, std::exception
        // ...
    }
    // ...
    return;
}

And

LRESULT CMainFrame::OnSequencerPostMessage( WPARAM wParam, LPARAM lParam )
{
    // ...
    bool *work_is_done = reinterpret_cast<bool*>(wParam);
    CSchedulerStep* step = reinterpret_cast<CSchedulerStep*>(lParam);

    // Handle business case ...
    // ...

    // Finally notify sequencer thread that the work is done
    work_is_done = true;
    g_sequencerJobCondition.notify_one();

    return true;
}