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:

  1. read myInt
  2. add 1 to what we just read
  3. 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?