How to lock an NSLock on a specific thread

grand-central-dispatch, ios, multithreading, nslock, objective-c

Solution

It is rare anymore that `NSLock` is the right tool for the job. There much better tools now, particularly with GCD; more later.

As you probably already know from the docs, but I'll repeat for those reading along:

Warning: The NSLock class uses POSIX threads to implement its locking behavior. When sending an unlock message to an NSLock object, you must be sure that message is sent from the same thread that sent the initial lock message. Unlocking a lock from a different thread can result in undefined behavior.

That's very hard to implement without deadlocking if you're trying to lock and unlock on different threads. The fundamental problem is that if `lock` blocks the thread, then there is no way for the subsequent `unlock` to ever run on that thread, and you can't `unlock` on a different thread. `NSLock` is not for this problem.

Rather than `NSLock`, you can implement the same patterns with `dispatch_semaphore_create()`. These can be safely updated on any thread you like. You can lock using `dispatch_semaphore_wait()` and you can unlock using `dispatch_semaphore_signal()`. That said, this still usually isn't the right answer.

Most resource contention is best managed with an operation queue or dispatch queue. These provide excellent ways to handle work in parallel, manage resources, wait on events, implement producer/consumer patterns, and otherwise do almost everything that you would have done with an `NSLock` or `NSThread` in the past. I highly recommend the Concurrency Programming Guide as an introduction to how to design with queues rather than locks.

Problem

I have a property `@property NSLock *myLock` And I want to write two methods: ``` - (void) lock ``` and ``` - (void) unlock ``` These methods lock and unlock `myLock` respectively and they need to do this regardless of what thread or queue called them. For instance, thread A might have called `lock` but queue B might be the one calling `unlock`. Both of these methods should work appropriately without reporting that I am trying to unlock a lock from a different thread/queue that locked it. Additionally, they need to do this synchronously.

Original source