Swift Data Race vs. Race Condition

Karthik
3 min readOct 15, 2021

--

Let’s talks about the concurrency issue in the multithreaded application.

Multithreading is one of the most important concepts in software engineering. It comes with complexity like race conditions and deadlock and thread-safety.

Race Condition

A race condition is an undesirable situation that occurs when a program attempts to perform two or more operations at the same time on shared resources.

Data Race

A data race occurs when one thread reading data from a shared object while another thread is writing to it.

Ensure ThreadSafe

Thread-safe is a concept in the context of multi-thread and it means any shared data is accessed by only one thread at any given time.

  • NSLock
  • Dispatch Semaphore
  • Serial Queue
  • Dispatch Barrier

These are some methods to ensure the thread-safety, each one has its own pros and cons. Let's create thread-safety using dispatch barrier, one of my all-time favorites in handling concurrency.

Dispatch Barrier

A dispatch barrier allows us to create a synchronization point within a concurrent dispatch queue. Normally, the queue acts just like a normal concurrent queue, When the barrier is executing, it acts as a serial queue that creates synchronization. Once the barrier finishes, the queue goes back to being a normal concurrent queue.

Sample architecture how barrier works

Real Use Case

In our project, we have user availability which helps to know the user state. The state can be changed every second, in such cases we tend to get n number of incoming availability, in peak hours the N will be huge. so it’s very important to protect the availability source (shared) from the race condition and concurrency issues. After so many iterations, we ended up choosing the DispatchBarrier to handle the synchronization.

Sample availability model
Sample cache that stores availability data

getAvailability offers concurrent reading since the dispatching queue is been marked as a concurrent queue. sync blocks the caller thread till the block execution completes. whereas on updateAvailability the dispatching queue is marked as async with barrier flag, which means that it will wait until every currently running block in the queue is finished executing before it executes. Other blocks will queue up behind it and be executed when the barrier dispatch is done.

custom serial queue is a bad choice for serialization, since barriers won’t do anything helpful since a serial queue executes one operation at a time anyway, custom concurrent queue is a great candidate for creating automaticity.

💡 NSCache provides default synchronization since it's from Obj-C.

Thread Sanitizer

Thread Sanitizer is a part of a family of LLVM tools that combines compile-time instrumentation and runtime monitoring to detect threading bugs in the code. It was introduced in Xcode 8 and has support for both Objective C and Swift. It can detect multiple types of threading bugs one of which is the Data Race Condition.

If your application has a Data Race Condition, Thread Sanitizer will detect it and log in to the console. (Note — Thread Sanitizer might not detect all Data Race Conditions in a single attempt. In that case, try executing the steps to reproduce multiple times)

Wrapping up:

In this article, we talk about a method for achieving thread safety. The idea behind every approach is the same, identify the critical code section and make it accessible by only one thread at a time.

I hope you enjoyed this article 😀. Please feel free to share, comment, and give some claps 👏👏. Stay tuned at this pace for more updates. Thanks for reading this.

You can follow me on Medium, LinkedIn, and Twitter

--

--