#include <iostream>
#include <chrono>
#include <string>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>

std::mutex m;
std::condition_variable cv;
std::atomic<int> ready( 0 );

void worker_thread1()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk( m );
    cv.wait( lk, [] { return ( ready==1 ); } ); // waits for ready to become 1
    // after the wait, we own the lock.
    std::cout << "Worker thread 1 started\n";
    ready = 2; // so 2nd thread continues
    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_all for details)
    lk.unlock();
    cv.notify_all();
}

void worker_thread2()
{
    std::unique_lock<std::mutex> lk( m );
    cv.wait( lk, [] { return ( ready==2 ); } ); // waits for ready to become 2
    std::cout << "Worker thread 2 started\n";
    ready = 3;
    lk.unlock();
    cv.notify_all();
}

int main()
{
    std::thread worker1( worker_thread1 );
    std::thread worker2( worker_thread2 );
    // send data to the worker thread
    {
        //std::lock_guard<std::mutex> lk( m ); // <<-- required by C++ standard
        ready = 1;
        std::cout << "main() signals data ready for processing\n";
    }
    //cv.notify_one(); // since only 1 worker is notified, program may deadlock if worker 2 is chosen
    cv.notify_all();
    // wait for the workers
    {
        std::unique_lock<std::mutex> lk( m );
        cv.wait( lk, [] { return ( ready==3 ); } );
    }
    std::cout << "Back in main()\n";
    worker1.join();
    worker2.join();
}