#include <iostream>
#include <mutex>
#include <thread>

#define SOL1

struct Box {
    explicit Box( int num ) : num_things {num} {}

    int num_things;
    std::mutex m;
};

void transfer( Box & from, Box & to, int num )
{
#ifdef SOL1
    // don't actually take the locks yet
    std::unique_lock<std::mutex> lock1( from.m, std::defer_lock );
    std::unique_lock<std::mutex> lock2( to.m, std::defer_lock );
    // do some work here - mutexes are unlocked
    std::lock( lock1, lock2 );
#endif

#ifdef SOL2
    std::lock( from.m, to.m );
    // make sure both already-locked mutexes are unlocked at the end of scope
    std::lock_guard<std::mutex> lock1( from.m, std::adopt_lock );
    std::lock_guard<std::mutex> lock2( to.m, std::adopt_lock );
#endif

    // lock both unique_locks without deadlock
    from.num_things -= num;
    to.num_things += num;
    // 'from.m' and 'to.m' mutexes unlocked in 'unique_lock' dtors
}

int main()
{
    Box acc1( 100 );
    Box acc2( 50 );
    std::cout << "Box 1 = " << acc1.num_things << std::endl;
    std::cout << "Box 2 = " << acc2.num_things << std::endl;
    std::thread t1( transfer, std::ref( acc1 ), std::ref( acc2 ), 20 );
    std::thread t2( transfer, std::ref( acc2 ), std::ref( acc1 ), 5 );
    t1.join();
    t2.join();
    std::cout << "Box 1 = " << acc1.num_things << std::endl;
    std::cout << "Box 2 = " << acc2.num_things << std::endl;
}