“gray and black laptop computer on surface” by Ales Nesetril on Unsplash

ConcurrentHashMap in Objective C

Prabal Chakraborty
Sep 3, 2018 · 4 min read

Since you’re here, chances are you’ve googled for a Objective C counterpart of the ConcurrentHashMap Data Structure that Java provides out of the box, and have found guidelines@ but not a fully formed code. For anyone not entirely well-versed with the Java ConcurrentHashMap, here’s what the Java documentation says:

"A hash table supporting full concurrency of retrievals and adjustable expected concurrency for updates"

What it essentially means for most use-cases is that this is a key-value store which is thread-safe such that multiple threads can be reading from / writing to this Data Structure (DS) simultaneously and the HashMap will maintain its data consistency. As is clear, Cocoa doesn’t provide a counterpart to this.

In the general world of Software Engineering, such a thing can be solved by one of the following approaches:

Locks (Oh, don’t we all hate locks!)
Just do one thing at a time, right? Use a @synchronized block to lock on an object — could be the DS object itself, and let only one thread operate on the DS at a time:

- (id)objectForKey:(id)aKey {
@synchronized(underlyingDict) {
return [underlyingDict objectForKey:aKey];
}
}
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
@synchronized(underlyingDict) {
[underlyingDict setObject:anObject forKey:aKey];
}
}

Yes, this solves the problem, by changing the problem statement itself! Remember we set out to have a DS which is accessible by multiple threads at the same time? So technically, putting a lock doesn’t let multiple threads access it simultaneously. Also, locking generally has pitfalls like potential deadlock situations and compromise on speed of access.

Let us rethink the locking solution. Is there a problem in multiple threads reading from the same object? What could go wrong? Nothing, as long as reading makes no changes to the data (it is called ‘reading’ for a reason, duh)! This means we can actually better the first approach by using the locking only for writes and not for reads, right? Wrong! Think about why?

If you’ve landed here after some amount of online searching, you’ll have found solutions where people suggest using a custom serial dispatch queue and making all reads and writes on that queue. This is effectively same as the locking approach mentioned above, because every operation (read/write) will happen sequentially, one after the other.

Concurrent reads, exclusive writes
This approach uses the age old guideline – make your reads fast and your writes accurate. What we intend is to let multiple parties (or threads) read the data concurrently, but ensure that writes are exclusive such that when a write is being executed, no other operation is being performed on that data at that time. In ObjC, this is achieved by utilising the concurrent dispatch queues provided by the GCD (or libdispatch) library of Cocoa.

We first have to create a concurrent dispatch queue for our use:

dispatch_queue_t accessQueue = dispatch_queue_create("com.pc.concurrentDictQueue", DISPATCH_QUEUE_CONCURRENT);

For reads, we use dispatch_sync on this queue, thus ensuring that reads take place synchronously, and are blocking:

dispatch_sync(accessQueue, ^{
id object = [underlyingDict objectForKey:aKey];
});

For writes, we use dispatch_barrier_async, which ensures that the block supplied to this queue is executed only after all the previously submitted blocks on that queue are completed, and also that this block is executed exclusively, so any blocks submitted after this block, will start executing only once the execution of this block is completed:

dispatch_barrier_async(accessQueue, ^{
[underlyingDict setObject:anObject forKey:aKey];
});

The object underlyingDict, as you may have already guessed, is a plain NSMutableDictionary. The complete code for this ConcurrentDictionary (that’s what I named it) can be found here.

Caution where due

There are a few things to keep in mind when using this DS in your work:

Initialisation: once creates, this dictionary handles concurrency on its own so it will make sure that your concurrent reads and writes perform as expected but you have to ensure that you create the instance of this dictionary in a thread-safe (or singleton) manner (see the example code on github above). It is a very common error that because there are multiple concurrent threads reading or writing to this DS, you may expose the creation of the dictionary to multiple threads thus ending up in a situation where one thread creates the object and starts using it, while another one is creating a new instance at the same time because when it last checked, the instance didn’t exist. Using the dispatch_once method is your best bet here.

Dispatch Queue: if you decide to not use my code but write your own based on these guidelines, even better. One word of caution there would be to create and use an exclusive concurrent dispatch queue (do not use global dispatch queues because you never know who is using them for what) for the purpose of the dictionary and not perform any other unrelated actions on that queue because that may slow down or even stall your data structure.

As this is my first contribution to Medium, I’d love to hear your thoughts and suggestions so please share them as comments. Cheers.

PS: If you liked the article, please support it with claps 👏 below. Cheers!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade