Reputation: 730
Below is the code. Inside the block I tried 3 cases. Only one case at a time rest are commented.
set = [[NSMutableSet alloc] init];
Person* p = [[Person alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
for (int index = 0; index < 100; index++) {
dispatch_async(queue, ^{
//Case 1: adding constant value each time
//[self->set addObject:@"0"];
//Case 2: adding new value on each call.
[self->set addObject:[NSNumber numberWithInt:index]];
//Case 3: Modifying custom object
// p.firstname = [NSString stringWithFormat:@"%d", index];
});
}
I know if I put a lock/@syncronize inside block it works fine.
Question 1: Why case one runs without any problem even when I tried with index range from 0 to 100000. Mutable set is not thread safe so it must be crashing somewhere. So why it works without synchronisation?
Question 2: As case 1 runs fine every time so case 2 also should run fine as same set is being modified. But it crashes on every run.
Question 3: Case 3 is not crashing even I am putting new value each time as in case second.
Note: all properties are nonatomic.
Case 2 crash logs :
**malloc: *** **error for object 0x6000028e9260: pointer being freed was not allocated****
Upvotes: 1
Views: 259
Reputation: 437917
First, let’s be clear, that none of this is thread-safe. Synchronization is needed if you’re really going to do this from multiple threads.
Second, you also should hesitate to ever draw conclusions from your inability to produce a particular crash. They are notoriously difficult to manifest. Plus, you are testing a very narrow set of behaviors, never testing whether reads are successful, whether the objects returned are internally consistent, etc.
With respect to adding the same object repeatedly to a set, this is not a great test because subsequent adds undoubtedly determine that the object is already in the set and therefore practically no mutation is happening.
With your custom object example, all you’re doing is mutating a single pointer, but never using it. Plus certain hardware features are going to make it hard to manifest any problem, anyway. It doesn’t mean that you don’t need synchronization, but only that it’s going to be exceeding difficult to manifest the problem.
Besides, synchronization of a custom object like a “person” generally needs to happen at a higher level of abstraction (e.g. if changing a person’s full name, contemporaneous reads really should wait for all three properties, first, middle, and last name, to finish, or else they you may catch it in some indeterminate state).
Bottom line, you really should synchronize your interaction with the mutable set and your mutable person object if you’re going to access them from multiple threads.
I’d suggest you investigate the thread sanitizer (TSAN, to its friends). So, in Xcode, go to “Editor” » “Scheme” » “Edit Scheme...” and turn on thread sanitizer:
This will help you identify unsafe access from multiple threads. For more information, see the Thread Sanitizer and Static Analyzer video.
But as Apple advises in the above video, “there’s no such thing as a ‘benign’ race.”
Upvotes: 4