#include <iostream>

#include <thread>
#include <mutex>

using namespace std;

/* LatexBeginDeadlock */
void deadlock(mutex& x, mutex& y)
{
  auto id = this_thread::get_id();
  
  lock_guard<mutex> lgx{x};
  cout << id << ": Have lock " << &x << endl;
  
  this_thread::yield(); // try to get bad luck here
  
  lock_guard<mutex> lgy{y};
  cout << id << ": Have lock " << &y << endl;

  cout << id << ": Doing stuff requiring both locks" << endl;
}
/* LatexEndDeadlock */

/* LatexBeginAvoid */
void no_deadlock(mutex& x, mutex& y)
{
  auto id = this_thread::get_id();

  // C++11, take locks
//  lock(x, y);
  // And arrange for automatic unlocking
//  lock_guard<mutex> lgx{x, adopt_lock};
//  lock_guard<mutex> lgy{y, adopt_lock};

  // C++17
  scoped_lock lock{x, y};
  cout << id << ": Have lock " << &x << " and " << &y << endl;
  
  cout << id << ": Doing stuff requiring both locks" << endl;
}
/* LatexEndAvoid */

/* LatexBeginMain */
int main()
{
  mutex A;
  mutex B;

  // references parameters have to be specified explicitly
  thread AB{no_deadlock, ref(A), ref(B)};
  thread BA{no_deadlock, ref(B), ref(A)};

  AB.join();
  BA.join();

  cout << "Main done" << endl;
  
  return 0;
}
/* LatexEndMain */