Tuesday, November 4, 2014

Concurrency problems in singleton design pattern - An objcective c example

Singleton pattern is a very common pattern in iOS. It is one of the easiest to understand. But if you do not fully consider the concurrency, you will come across bugs which are extremely difficult to find. Have a look the following code which looks completely normal.
+ (instancetype)sharedInstance    
{
    static SomeClass *obj = nil;
    if (!obj) {
        obj = [[SomeClass alloc] init];
    }
    return obj;
}
Looks normal right? But really it is not the case.
Here the if condition branch is not thread safe. If this method is invoked with multiple threads, this may happen. Assume you have two threads(A and B). Now A enters the if block and a context switch occurs before
obj = [[SomeClass alloc] init];
is executed. Next thread B enters the if block and allocates an instance of the singleton and exit. Then thread A gets the chance. It also starts inside the if block and creates the instance. Now you have two instances. Clearly this is not what you need in singleton pattern.

Solution
+ (instancetype)sharedIinstance
{
    static SomeClass *obj = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        obj = [[SomeClass alloc] init];
    });
    return obj;
}

Here dispatch_once() executes the block only a one time in a thread safe manner. So different threads will not be able to access the critical section as I showed earlier.
This is not the only problem with singleton. Here only instantiation is thread safe. A Class may have mutable objects. In that case we need to consider about the thread safety status of those objects. Otherwise reader writer problems may occur.
Assume you have a NSMutableArray inside the singleton class and you have read and write operations in to that NSMutableArray object. you need to do two things in order to ensure thread safety. Here there is a possibility for a reader/writer problem scenario.
What is the reader/writer problem?
Assume you are going to make an online purchase and there is only one item left. As long as you are doing only read operations it is ok. But if you do any database write operations there may be problems. If someone completes a transaction while you are doing the transaction a problem will occur. It is called the reader/writer problem.
With apple GCD we have a very easy solution for that. GCD has an implementation of a reader/writer queue. GCD manages everything for us. A dispatch barrier creates a synchronization point inside a concurrent dispatch queue. GCD ensures that the submitted block is the only item executed on the specified queue for that particular time. All items submitted to the queue prior to the dispatch barrier must complete before the execution of the block. After the block is finished queue returns to the its specified implementation.
We have a NSMutableArray as follows.
@property (nonatomic, strong) NSMutableArray array;

And read and write operations as follows.
- (void)addObject:(NSObject *)obj
{
    if (obj) {   
     [_array addObject:obj]; 
    }
}

- (NSArray *)getObj
{
    NSArray *array = [NSArray arrayWithArray:_array]; 
    return array;
}

Add this to the header file
@property (nonatomic, strong) dispatch_queue_t concurrentQueue;

Then when you add to the NSMutableArray instance, the adding object operation should be added with a dispatch barrier.
- (void)addObject:(NSObject *)obj
{
    if (photo) { 
        dispatch_barrier_async(self.concurrentQueue, ^{  
            [_array addObject:obj]; 
        });
    }
}

Also when you read data from the mutable array also you should do it via a serially dispatched block as follows.
- (NSArray *)getObj
{
    __block NSArray *array; 
    dispatch_sync(self.concurrentQueue, ^{ 
        array = [NSArray arrayWithArray:_array]; 
    });
    return array;
}

Now your singleton is thread safe!! For any query, feel free to contact me via my linkedin profile. Happy coding!!!