Ordered By Address

This transfer function uses the standard trick of ordering two locks by their addresses so no pair of callers can build a cycle:

struct Account { std::mutex m; long cents; };

void transfer(Account& from, Account& to, long amount) {
    if (&from.m < &to.m) {
        from.m.lock();
        to.m.lock();
    } else {
        to.m.lock();
        from.m.lock();
    }
    from.cents -= amount;
    to.cents   += amount;
    from.m.unlock();
    to.m.unlock();
}

The ordering is right and two threads calling transfer(a, b, 100) and transfer(b, a, 250) never deadlock. One caller still hangs forever. Which one?

A

Any call at all, because the comparison uses &from.m instead of &from, so the ordering can differ between two calls on the same pair.

B

transfer(a, a, 0). Both branches then lock the same mutex twice, and std::mutex is not recursive, so the second lock() is undefined behavior that in practice blocks on itself.

C

Any call where amount exceeds from.cents, because the balance goes negative and the second unlock is skipped.

D

A call made on a machine where the compiler places both mutexes in the same cache line, which defeats the address comparison.