Lock Impl.

Implementing Locks

Optional readings for this topic from Operating Systems: Principles and Practice: Section 5.7.

How to implement locks and condition variables (inside the operating system)?

Uniprocessor solution: just disable interrupts.

class Lock {
    Lock() {}
    bool locked = false;
    ThreadQueue q;
};

void Lock::lock() {
    intrDisable();
    if (!locked) {
        locked = true;
    } else {
        q.add(currentThread);
        blockThread();
    }
    intrEnable();
}

void Lock::unlock() {
    intrDisable();
    if (q.empty() {
        locked = false;
    } else {
        unblockThread(q.remove());
    }
    intrEnable();
}

Implementing locks on a multi-core machine: turning off interrupts isn't enough.

  • Hardware provides some sort of atomic read-modify-write instruction, which can be used to build higher-level synchronization operations such as locks.
  • Example: exchange: atomically read memory value and replace it with a given value: returns old value.

Attempt #1:

class Lock {
    Lock() {}
    std::atomic<bool> locked(false);
};

void Lock::lock() {
    while (locked.exchange(true)) {
        /* Do nothing */
    }
}

void Lock::unlock() {
    locked = false;
}

Attempt #2:

class Lock {
    Lock() {}
    std::atomic<bool> locked(false);
    ThreadQueue q;
};

void Lock::lock() {
    if (locked.exchange(true)) {
        q.add(currentThread);
        blockThread();
    }
}

void Lock::unlock() {
    if (q.empty() {
        locked = false;
    } else {
        unblockThread(q.remove());
    }
}

Attempt #3:

class Lock {
    Lock() {}
    bool locked = false;
    ThreadQueue q;
    std::atomic<bool> spinlock;
};

void Lock::lock() {
    while (spinlock.exchange(true)) {
        /* Do nothing */
    }
    if (!locked) {
        locked = true;
        spinlock = false;
    } else {
        q.add(currentThread);
        spinlock = false;
        blockThread();
    }
}

void Lock::unlock() {
    while (spinlock.exchange(true)) {
        /* Do nothing */
    }
    if (q.empty() {
        locked = false;
    } else {
        unblockThread(q.remove());
    }
    spinlock = false;
}

Attempt #4:

class Lock {
    Lock() {}
    bool locked = false;
    ThreadQueue q;
    std::atomic<bool> spinlock;
};

void Lock::lock() {
    while (spinLock.exchange(true)) {
        /* Do nothing */
    }
    if (!locked) {
        locked = true;
        spinlock = false;
    } else {
        q.add(currentThread);
        currentThread->state = BLOCKED;
        spinlock = false;
        redispatch();
    }
}

void Lock::unlock() {
    while (spinlock.exchange(true)) {
        /* Do nothing */
    }
    if (q.empty() {
        locked = false;
    } else {
        unblockThread(q.remove());
    }
    spinlock = false;
}

Final solution:

class Lock {
    Lock() {}
    bool locked = false;
    ThreadQueue q;
    std::atomic<bool> spinlock;
};

void Lock::lock() {
    intrDisable();
    while (spinlock.exchange(true)) {
        /* Do nothing */
    }
    if (!locked) {
        locked = true;
        spinlock = false;
    } else {
        q.add(currentThread);
        currentThread->state = BLOCKED;
        spinlock = false;
        redispatch();
    }
    intrEnable();
}

void Lock::unlock() {
    intrDisable();
    while (spinlock.exchange(true)) {
        /* Do nothing */
    }
    if (q.empty() {
        locked = false;
    } else {
        unblockThread(q.remove());
    }
    spinlock = false;
    intrEnable();
}