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

using namespace std;
using namespace std::chrono;

int main() 
{
  auto worker = [](mutex& m,
                   atomic<bool>& done,
                   atomic<bool>& main_done,
                   condition_variable& notify_main,
                   condition_variable& wait_main,
                   uint32_t workload)
  {
    unique_lock<mutex> ul(m);
    while (true)
    {
      while (main_done == false)
        wait_main.wait(ul, [&]{ return main_done.load(); });

      this_thread::sleep_for(milliseconds(workload));
      done.store(true);
      cout << this_thread::get_id() << " job done." << endl;

      notify_main.notify_one();
    }
  };

  //----------------------------------------------------------------------

  mutex main_m1, main_m2;
  condition_variable cv, cv1, cv2;
  atomic<bool> job1_done(false), job2_done(false), main_job(false);

  thread th1(worker, std::ref(main_m1), std::ref(job1_done), 
             std::ref(main_job), std::ref(cv1), std::ref(cv), 300);
  thread th2(worker, std::ref(main_m2), std::ref(job2_done), 
             std::ref(main_job), std::ref(cv2), std::ref(cv), 400);

  th1.detach();
  th2.detach();

  //----------------------------------------------------------------------

  mutex m1, m2;
  unique_lock<mutex> ul1(m1), ul2(m2);
  high_resolution_clock::time_point p = high_resolution_clock::now();

  while (true)
  {
    main_job = true;
    cv.notify_all();

    this_thread::sleep_for(milliseconds(100));
    cout << "main job done." << endl;

    while (job1_done == false || job2_done == false)
    {
      cv1.wait(ul1, [&]{ return job1_done.load(); });
      cv2.wait(ul2, [&]{ return job2_done.load(); });
    }

    job2_done = false;
    job1_done = false;
    main_job = false;

    double dur = duration_cast<milliseconds>(high_resolution_clock::now() - p).count();
    p = high_resolution_clock::now();
    cout << dur << endl;
  }

	return 0;
}