Synchronization 2: Critical sections, serializability, lock granularity

CS 61 Bank

Basic account operations

C++ sketch

class acct {
    long bal;

    void deposit(long amt) {
        bal += amt;
    }

    bool try_withdraw(long amt) {
        if (bal < amt) {
            return false;
        }
        bal -= amt;
        return true;
    }
};

Concurrent access

Atomics eliminate data races

class acct {
    std::atomic<long> bal;

    // ... same `deposit` and `try_withdraw`
};

Atomics don’t eliminate race conditions

Race condition example

Serializability

Critical sections

How to delimit a critical section

Global mutex

std::mutex bm;

class acct {
    std::atomic<long> bal;

    void deposit(long amt) {
        bm.lock();
        bal += amt;
        bm.unlock();
    }

    bool try_withdraw(long amt) {
        bm.lock();
        if (bal < amt) {
            bm.unlock();
            return false;
        }
        bal -= amt;
        bm.unlock();
        return true;
    }
};

Functions and lock state

Guard pattern

Mutexes and atomics

std::mutex bm;

class acct {
    long bal;

    void deposit(long amt) {
        std::unique_lock guard(bm);
        bal += amt;
    }

    bool try_withdraw(long amt) {
        std::unique_lock guard(bm);
        if (bal < amt) {
            return false;
        }
        bal -= amt;
        return true;
    }
};

Lock granularity

Fine-grained account locking

class acct {
    long bal;
    std::mutex am;

    void deposit(long amt) {
        std::unique_lock guard(am);
        bal += amt;
    }

    bool try_withdraw(long amt) {
        std::unique_lock guard(am);
        if (bal < amt) {
            return false;
        }
        bal -= amt;
        return true;
    }
};