1

I have a Program class and a Browser class.

Inside my Program::Run(), I launch the Browser to start in a separate thread. However, before I continue with my Run() method, I want to wait for a certain part of the Browser to initialize, thus I need to check if a variable has been set in the browser object.

Used as the thread for the browser

int Program::_Thread_UI_Run() {
  ...
  return Browser->Run();
}

I am using async to run the browser thread and retrieve its return value when it is finished.

int Program::Start() {
    std::unique_lock<std::mutex> lck(mtx);
    auto t1 = std::async(&Program::_Thread_Browser_Run, this);
    cv.wait(lck);
     ... when wait is released, do stuff

    // Thread finishes and returns an exit code for the program
    auto res1 = f1.get();

// return res1 as exit code.
}

Browser.cpp class

int Browser::Run()
{
    // Initialize many things
    ...

    m_Running = true; 
    cv.notify_all(); // Notify the waiter back in Program

    // This will run for as long as the program is running
    while (m_Running)
    {
      ... browser window message loop
    }
    return exit_code;
}

I have problems setting this up. The program is crashing :/ Do I pass the mutex variable to everything using it? Or just recreate it in every function body? What about the conditional_variable?

With the current setup the program crashes: The exception Breakpoint A breakpoint has been reached. (0x80000003) occured in the application at location 0x107d07d6.

Hints and help is appreciated

Edit: Updated code to match new suggestions

In browser's .h file: std::atomic_bool m_Running;

int Browser::Run(std::condition_variable& cv)
{
    int exit_code = 0;
    // Set up, and attain the desired state:
    ...
    m_Running = true;
    cv.notify_all();
    while (m_Running)
    {
        // Process things etc
    }
    return exit_code;
}

int Program::Start()
{
    std::mutex m;
    std::condition_variable cv;

    auto t1 = std::async(&Program::_Thread_UI_Run, this, std::ref(cv));
    std::unique_lock<std::mutex> lock(m);
    cv.wait(lock);


    .... stuff


    return t1.get();
}

I have a logger that helps me keep track of how the program is running. By placing logger calls in crucial places in the code I was able to confirm that the program waits appropiately before continuing. However I still get prompted with

The exception Breakpoint A breakpoint has been reached. (0x80000003) occured in the application at location 0x107d07d6.

By commenting out //cv.wait(lock); the program resumes to work.. :/ Why would waiting making it crash like that?

Alx
  • 651
  • 1
  • 9
  • 26
  • 2
    You're definitely looking towards [`std::condition_variable`](http://en.cppreference.com/w/cpp/thread/condition_variable). It's very simple to use: use `wait()` in your main thread where you're waiting for the thread to set the bool, and use `notify_all()` in your thread once the `bool` is set – Tas Jan 06 '16 at 09:25
  • @Tas Thank you for the input. I have updated my problem to better describe what is happening when I am attempting to implement condition variables. – Alx Jan 06 '16 at 11:45
  • It's hard to understand your code. What does the mutex protect? – David Schwartz Jan 07 '16 at 11:02
  • @DavidSchwartz What I want to achieve is that Program::Start() will begin, and then wait for a signal given by Browser::Run before it continues, to make sure that everything in the other thread was initialized beforehand. – Alx Jan 07 '16 at 12:37
  • @MadsM Okay. And where in the code do you keep track of whether the signal has been sent or not? That's what the mutex is supposed to protect, no? (Do you understand why a condition variable must be associated with a mutex?) – David Schwartz Jan 07 '16 at 18:08

1 Answers1

3

You definitely want to use std::condition_variable. It allows you to signal other threads once an operation has complete, so in your case, once the bool has been set:

Browser::Run()
{
    // Set some things up, make sure everything is okay:
    ...
    m_Running = true; // Now the thread is, by our standards, running*
    // Let other threads know:
    cv.notify_all();
    // Continue to process thread:
    while (m_Running)
    {
    }
}

And then in your main / other thread:

auto t1 = std::async(&Program::_Thread_Browser_Run, this);
// Wait until we know the thread is actually running. This will pause this thread indefinitely until the condition_variable signals.
cv.wait();

You should pass the std::condition_variable into any function using it, so your code would look more like:

int Browser::Run(std::condition_variable& cv)
{
    int exit_code = 0;
    // Set up, and attain the desired state:
    ...
    m_Running = true;
    cv.notify_all();
    while (m_Running)
    {
        // Process things etc
    }
    return exit_code;
}

int Program::Start()
{
    std::mutex m;
    std::condition_variable cv;
    auto t1 = std::async(&Program::_Thread_UI_Run, this, std::ref(cv));
    std::unique_lock<std::mutex> lock(m);
    // Wait until the browser is in the desired state
    cv.wait(lock);
    // The thread has signalled. At this point, Browser::m_Running = true
    // Wait for the browser to exit, and then propagate its exit code
    return t1.get();
}

@Richard Hodges raises an excellent point in the comments, which I overlooked: m_Running needs to be std::atomic (or have locking around its use) otherwise both threads may try to use it once. std::condition_variable is thread-safe and doesn't require locking around it.

*Of course the thread is running at that point, I just mean it's in the state you desire

Tas
  • 7,023
  • 3
  • 36
  • 51
  • 2
    unless m_Running is atomic you will need to put a lock around access to it in order to force a memory fence. If you do not, it is undefined whether the other thread will actually see the change in m_Running. – Richard Hodges Jan 06 '16 at 09:55
  • Thank you for the comments. Your setup of Browser::Run() is exactly what I had in mind. However I am not sure HOW to perform cv.wait in my Program class, since normally I would check the status by calling `bool _IsBrowserRunning() { return Browser->IsRunning(); }` but it seems like I cannot do that inside wait() ? – Alx Jan 06 '16 at 09:59
  • @MadsM I've updated my answer to hopefully help further. You won't need to query if the `Browser` is running, because your thread will remain locked until it is. – Tas Jan 07 '16 at 02:44
  • `std::condition_variable` is just a **much** better way of doing `while (!_IsBrowserRunning());` – Tas Jan 07 '16 at 02:46
  • @Tas Thank you very much for the information. I have updated my post to show what happens with these new changes. – Alx Jan 07 '16 at 10:40