What do locks lock?
I just read an interesting question:
What part of memory gets locked by a lock when .lock() is called? Is it just the function or is it the whole program that gets locked?
And thought that would be a fun one to answer. Let’s start with a controversial headline:
A Lock Actually doesn’t Lock Anything
Yes, you heard right! A lock is a tool you can use in your code to let other parts of
your code know when it is OK to modify a variable. That’s all it does. It doesn’t “lock
memory” or “lock the function”. It basically functions like a special kind of Boolean
that says whether something is OK to modify, or not.
This is not how a Lock is actually implemented, but let’s for a second think of a lock as
just a wrapper around a Boolean
:
class Lock {
var okToModify = true
func lock() {
while !okToModify { // Did another thread lock this?
// wait until the other thread unlocks its hold.
}
okToModify = false
}
func unlock() {
okToModify = true
}
}
Now, if we wanted to protect an int from access by several threads, we would declare it as:
var myInt = 0
let myIntLock = Lock()
And wherever we want to access myInt, we do:
myIntLock.lock()
myInt += 1
myIntLock.unlock()
Of course, the above code is nonsense. If another thread can cause issues by modifying
myInt
while the += 1
is happening, it can even more easily cause issues by getting in
between the while(!okToModify)
and the okToModify = false
in the lock()
function
above.
Why? because += 1 is actually 3 machine instructions:
- read
myInt
- add
1
to what we just read - write the result back into
myInt
And that is why lock classes actually use special magic machine instructions that can do
the compare-and-change in one operation. And no other thread can change the okToModify
variable with compare-and-change while another is already doing it. It’s just compiler
magic.
So while a Boolean
can not be used as a lock, a lock is a special case of a Boolean
,
in the most simple use of locks. So for example, in Swift, there is a special object
called a DispatchSemaphore
that you could use to implement a lock. And this semaphore
does all the magic for you:
class Lock {
let semaphore = DispatchSemaphore(value: 1)
func lock() {
semaphore.wait()
}
func unlock() {
semaphore.signal()
}
}
Neat, huh?