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();
}