Search code examples
objective-crace-condition

How to understand the member testing and adding race condition of NSMutableSet?


When I reading the book 《Objective-C 2.0 Phrasebook》, There is something about NSMutableSet that I don't really understand, as follow,

There is a potential race between testing whether the string is in the set and adding the new one if it isn’t. A thread-safe version of this would require you to acquire a lock after the -member: call, send this message again, and then finally release the lock after adding the new object.

I don't understand why I should

send this message again

Does it mean I should make the -member: call again? Please help me. Any code examples will be much appreciated.


Solution

  • In Objective-C, an expression like [mySet member:candidate] sends the “message” named member: to mySet (the “receiver”) with the argument candidate. It’s often referred to as a “method call” or just a “call”, but it’s more properly called “sending a message” or a “message send” because there’s considerably more machinery (and opportunity for customization) in an Objective-C message send than in the method calls of more common languages like C++ or Java.

    So yes, when the book says “send this message again”, it means to do [mySet member:candidate] again. I haven’t seen the book, but your quote is describing the “double-checked locking” pattern.

    However, what the book is describing is not sufficient to avoid race conditions if you’re accessing the same NSMutableSet from multiple threads (and not needed if you only access the NSMutableSet from a single thread). The problem is that, if you say [mySet addObject:newMember] on thread A, it is not safe to access mySet simultaneously from thread B. During the execution of the addObject: method, the set may be in an internally-inconsistent state that could (for example) cause a simultaneous member: call on another thread to crash. You must lock read accesses to mySet in addition to write accesses, so the double-checked locking pattern isn’t sufficient protection for sharing an NSMutableSet across threads.

    The best way to handle this is to use a dispatch_queue_t to manage exclusive access to the NSMutableSet, and not worry about anything more sophisticated unless the profiler tells you that it’s a bottleneck.