#include <array>
#include <iostream>
#include <mutex>
#include <stdexcept>
#include <thread>
struct Account {std::mutex mutex; int amount=2000;};
struct InjectedFailure {};
bool transfer(Account& from,Account& to,int amount) {
    if(amount<0) return false;
    if(&from==&to) return true;
    std::scoped_lock lock(from.mutex,to.mutex);
    if(from.amount<amount) return false;
    from.amount-=amount; to.amount+=amount; return true;
}
void transfer_both(Account& a,Account& b,bool inject_failure=false) {
    std::array<std::thread,2> workers;
    struct JoinAll {
        std::array<std::thread,2>& threads;
        ~JoinAll() {for(auto& t:threads) if(t.joinable()) t.join();}
    } join_all{workers};
    workers[0]=std::thread([&]{for(int i=0;i<1000;++i) if(!transfer(a,b,1)) std::terminate();});
    // Explicit injection, not a claim that the machine exhausted thread resources.
    if(inject_failure) throw InjectedFailure{};
    workers[1]=std::thread([&]{for(int i=0;i<1000;++i) if(!transfer(b,a,1)) std::terminate();});
    for(auto& worker:workers) worker.join();
}
int main() {
    Account a,b;
    if(!transfer(a,a,1) || transfer(a,b,-1)) throw std::runtime_error("validation");
    transfer_both(a,b);
    if(a.amount!=2000 || b.amount!=2000) throw std::runtime_error("conservation");
    Account partial_a,partial_b; bool caught=false;
    try {transfer_both(partial_a,partial_b,true);} catch(const InjectedFailure&) {caught=true;}
    if(!caught || partial_a.amount!=1000 || partial_b.amount!=3000)
        throw std::runtime_error("join on creation failure");
    std::cout<<"balances="<<a.amount<<','<<b.amount<<" total="<<a.amount+b.amount<<'\n';
}
