Avoid Deadlock with Semaphores

Stop-Wait-Go

Image for post
Image for post
Photo by Drew Hays on Unsplash

You might well be familar with GCD, but what if you wish to make sure that you never enter a deadlocked state?

Read on!

Difficulty: Beginner | Easy | Normal | Challenging

This article has been developed using Xcode 11.4.1, and Swift 5.2.2

Updated for Xcode 11.5

The importance

The Readers-Writers Problem is an important one in Computer Science.

In your iOS application you may have several threads that wish to access a shared resource (the cannonical example for this is a file).

Image for post
Image for post
Photo by Maksym Kaharlytskyi on Unsplash

You would not want to be in a situation where the file can be written on at the same time as it is being read — imagine that *during* the reading process the file is written. This would mean that we couldn’t be certain of what we were reading; perhaps half of the original file and half of the updated file — what a disaster!

Image for post
Image for post
Photo by Sarah Kilian on Unsplash

The practical example

For each of these examples two `queues` are going to be used as follows:

Image for post
Image for post
Click for Gist

now if you wish to follow along in Playgrounds you’ll need to import PlaygroundSupport and add the indefiniteExecution line PlaygroundPage.current.needsIndefiniteExecution = true.

With that said, we can then create a naive function that attempts to write to the console from both queues (which usually use a single thread each, but this is not guarenteed):

Image for post
Image for post
Click for Gist

now depending on your machine, what is going on in the world and other indecipherable variables you “might” get all of the userInteractive work items complete before the default priority items.

Image for post
Image for post
Image for post
Image for post
A sample output

But this isn’t guarenteed. This isn’t a good state to be in — and what if we must have User finished before Default?

The naive solution

To control access (in this case printing to the console) be could block access using a Boolean. No problem!

Image for post
Image for post
Click for Gist

But there is a problem

Image for post
Image for post
The problem

We aren’t guaranteed that we are using the same thread when we access the function. Poor stuff!

This will *never* pass code review my friend!

The solution: A semaphore

A semaphore limits the amount of concurrent work that can be completed at the same time.

A semaphore simply keeps a count (positive) and contains two operations which must be thread-safe as it empties a queue of threads awaiting the resource in question.

  • wait: If the value is >0, decrement else block
  • signal: If threads are waiting then unblock one, else increment the value

When we set up a semaphore we pass a an initial value to the Semaphone — in this example we will use 1.

Image for post
Image for post
performwork

So semaphore.wait enables waiting for the resource to become avaliable, and then releases with semaphore.signal()

Image for post
Image for post

That’s more like it!

Conclusion

So a semaphore provides nothing more than a variable that can be incremented and decremented in a thread-safe manner. By helping you manage a multithreaded environment, a semaphore helps you manage complexity — and even provide an easy to use public API to build.

Now you’ve got the tool, what are you waiting for?

If you’ve any questions, comments or suggestions please hit me up on Twitter

Why not sign up to my newsletter

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store