enumerateKeysAndObjectsUsingBlock: can I be sure it is called on the same thread? - ios

Lately I am using enumerateKeysAndObjectsUsingBlock: and today I have hesitated when one of my colleague at work place pointed out that this enumeration method can be called from separate thread and my code may not work as expected. He even suggested I should use fast enumeration. Problem is I really like enumerateKeysAndObjectsUsingBlock: and dislike fast enumeration due to its nature when it comes to dictionary.
my method looked as following
- (NSArray *)someMethod
{
__block NSMutableArray *myArray = [NSMutableArray array];
[self.myDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[myArray addObject:obj];
}
return myArray;
}
Can I be sure that myArray will always return the expected values (assuming self.myDictionary is not empty) and it will be always called on the same thread as the someMethod?
I am aware there is a method enumerateKeysAndObjectsWithOptions:usingBlock: and calling it with NSEnumerationConcurrent option will run enumeration simultaneously on multiple threads.
But I can't find any documentation regarding enumerateKeysAndObjectsUsingBlock.
The same refers to enumerateObjectsUsingBlock: used on array.

As you observe, unless you call enumerateKeysAndObjectsWithOptions with the NSEnumerationConcurrent option, you can be assured that they won't be performed concurrently. As to whether the non-concurrent rendition of enumerateKeysAndObjects runs on the same thread or not (though, I'd wager it does), it doesn't really matter if it didn't run on the same queue because this is run synchronously with respect to the thread that you called the enumeration method. Bottom line, your coworker's thread-related concerns are without merit.
I would note, though, that in my anecdotal testing, these enumeration methods are slower than fast enumeration. If speed is of paramount interest (e.g. especially if using huge dictionaries/arrays), then you might want to employ fast enumeration. In most scenarios, though, the difference is not material.

Related

Can a bool var be safely accessed across threads?

This question pertains to Obj-C as the code is Obj-C, but I'd like to understand the difference (if any) in Swift too.
The golden rule in iOS that you shouldn't access an object across threads without using dispatch apis (or other) because this can lead to race conditions and other 'bad things'..
Is it safe however, to access a bool var from multiple threads? Since a bool var can only have one of two states, does that mean the following is always safe:
#property(nonatomic) BOOL processing;
-(void)callbackWithData(NSData *)data {
if (_processing) {
return;
}
// set from a background thread here
_processing = YES;
NSString *res = [self doSomeWorkThatReturnsString:data];
dispatch_async(dispatch_get_main_queue(), ^{
_someOtherCallback(res)
// set from the main thread here
_processing = NO;
});
}
callbackWithData gets called 1 or more times, sometimes in quick succession, on a background thread(s). Thus the _processing check to prevent the _someOtherCallback block from being called multiple times.
Maybe changing the property definition to atomic and using the synthesized getter/setter would be safer, but the question about direct access to the bool var still stands.
EDIT: To be clear, the atomic thing is a side question. My question is about where or not the code as shown is safe, or might it somehow produce memory corruption or a race condition/deadlock/other
From my knowledge, atomic properties will generate accessor code that locks the getter versus the setter call. This is only important for retain (and maybe copy) properties, since all others will just simply assign/return a value without prior checking. They won't help you in a more complex scenario, like the one you depicted in your code, when you first check the value and then do something depending on it.
Especially, if your callbackWithData is called by mutiple threads, it might evaluate _processing and see false, but and direcly after this, another thread my set _processing to true. No atomic could have helped you here.
If this might be a problem, you ought to use #synchronized(...) or some NSLock in your callbackWithData to lock the complete / most important parts of your method.

-allKeys on background thread results in error: __NSDictionaryM was mutated while being enumerated

I've come across an interesting issue using mutable dictionaries on background threads.
Currently, I am downloading data in chunks on one thread, adding it to a data set, and processing it on another background thread. The overall design works for the most part aside from one issue: On occasion, a function call to an inner dictionary within the main data set causes the following crash:
*** Collection <__NSDictionaryM: 0x13000a190> was mutated while being enumerated.
I know this is a fairly common crash to have, but the strange part is that it's not crashing in a loop on this collection. Instead, the exception breakpoint in Xcode is stopping on the following line:
NSArray *tempKeys = [temp allKeys];
This leads me to believe that one thread is adding items to this collection while the NSMutableDictionary's internal function call to -allKeys is enumerating over the keys in order to return the array on another thread.
My question is: Is this what's happening? If so, what would be the best way to avoid this?
Here's the gist of what I'm doing:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
for (NSString *key in [[queue allKeys] reverseObjectEnumerator]) { //To prevent crashes
NEXActivityMap *temp = queue[key];
NSArray *tempKeys = [temp allKeys]; //<= CRASHES HERE
if (tempKeys.count > 0) {
//Do other stuff
}
}
});
You can use #synchronize. And it will work. But this is mixing up two different ideas:
Threads have been around for many years. A new thread opens a new control flow. Code in different threads are running potentially concurrently causing conflicts as you had. To prevent this conflicts you have to use locks like #synchronized do.
GCD is the more modern concept. GCD runs "on top of threads" that means, it uses threads, but this is transparent for you. You do not have to care about this. Code running in different queues are running potentially concurrently causing conflicts. To prevent this conflicts you have to use one queue for shared resources.
You are already using GCD, what is a good idea:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
The same code with threads would look like this:
[[NSThread mainThread] performSelector:…];
So, using GCD, you should use GCD to prevent the conflicts. What you are doing is to use GCD wrongly and then "repair" that with locks.
Simply put all accesses to the shared resource (in your case the mutable dictionary referred by temp) into on serial queue.
Create a queue at the beginning for the accesses. This is a one-timer.
You can use one of the existing queues as you do in your code, but you have to use a serial one! But this potentially leads to long queues with waiting tasks (in your example blocks). Different tasks in a serial queue are executed one after each other, even there are cpu cores idle. So it is no good idea to put too many tasks into one queue. Create a queue for any shared resource or "subsystem":
dispatch_queue_t tempQueue;
tempQueue = dispatch_queue_create("tempQueue", NULL);
When code wants to access the mutable dictionary, put it in a queue:
It looks like this:
dispatch_sync( tempQueue, // or async, if it is possible
^{
[tempQueue setObject:… forKey:…]; // Or what you want to do.
}
You have to put every code accessing the shared resource in the queue as you have to put every code accessing the shared resource inn locks when using threads.
From Apple documentation "Thread safety summary":
Mutable objects are generally not thread-safe. To use mutable objects
in a threaded application, the application must synchronize access to
them using locks. (For more information, see Atomic Operations). In
general, the collection classes (for example, NSMutableArray,
NSMutableDictionary) are not thread-safe when mutations are concerned.
That is, if one or more threads are changing the same array, problems
can occur. You must lock around spots where reads and writes occur to
assure thread safety.
In your case, following scenario happens. From one thread, you add elements into dictionary. In another thread, you accessing allKeys method. While this methods copies all keys into array, other methods adds new key. This causes exception.
To avoid that, you have several options.
Because you are using dispatch queues, preferred way is to put all code, that access same mutable dictionary instance, into private serial dispatch queue.
Second option is passing immutable dictionary copy to other thread. In this case, no matter what happen in first thread with original dictionary, data still will be consistent. Note that you will probably need deep copy, cause you use dictionary/arrays hierarchy.
Alternatively you can wrap all points, where you access collections, with locks. Using #synchronized also implicitly create recursive lock for you.
How about wrapping where you get the keys AND where you set the keys, with #synchronize?
Example:
- (void)myMethod:(id)anObj
{
#synchronized(anObj)
{
// Everything between the braces is protected by the #synchronized directive.
}
}

NSMutableArray mutation crash

I'm facing "Collection was mutated while being enumerated" crash in dispatch_async method. This is the code:
- (void) addCutObjectFromObject: (Object *) object {
dispatch_async(self.objectsQueue, ^{
ObjectCut *objectCut = [[[ObjectCut alloc] init] autorelease];
objectCut.objectId = object.uid;
objectCut.objectName = object.internal.name;
#synchronized(self.shownChannelsArray) {
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
}
});
}
Dispatch_queue is created in the init method like this:
_objectsQueue = dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_retain(_objectsQueue);
The code crashes without synchronized block. As far as I know, it should not crash, because there is no loop, and blocks should be added one-by-one waiting for their queue. Can someone explain me, why that is happening?
dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT) creates a concurrent queue. Arbitrarily many blocks dispatched to that queue may execute simultaneously. It's likely that containsObject: uses enumeration internally to do a linear search. Therefore one block is in the middle of containsObject: while another performs addObject:. NSMutableArrays are, like the other Foundation mutable objects, not guaranteed to be thread safe.
At the very least you want to use DISPATCH_QUEUE_SERIAL. Unless the order is really important to you or your objects don't implement hash, isEqual:, etc, you should probably also just use a set rather than sort of manually recreating one with an array.
Aren't you calling addCutObjectFromObject: from a loop or an enumeration ?
If It is, you may want to check this part.
Ah a tricky one.. from my knowledge (and I may be a bit off, so others, feel free to follow up, but this poor chap doesnt have any responses yet..
so when this executes, the async block will not run right away. It will first be scheduled, I would imagine you are calling this addCutObjectFromObject method more than once. Let's say for example sake that you are calling this method 5 times. Now think about threads. The first call gets scheduled, eventually begins running, while the second call gets scheduled, eventually starts running, etc.. and this is where the problem starts and I believe your culprit is in containsObject and addObject. Under the hood, containsObject is iterating through the array. Since there are more than 1 thread executing, while one thread is iterating the array, another could be calling addObject. Thus generating your crash of 'collection was mutated while being iterating'
Two options for solutions
1) in containsObject, check against a copy of the array rather than the array itself (this is a bad approach because it requires duplicating of the content of the array, BUT it is easy to put in, and for small array sizes, could be a quick fix)
if (![self.objectsArray containsObject: objectCut]) =>
if (![[NSArray arrayWithArray:self.objectsArray] containsObject: objectCut])
Again, this should stop your sporadic crashes, but this is a BAD SOLUTION (because of the memory duplication)
2) create a temporary array to add the objects to, and once all threads are finished, add the contents of the temporary array to self.objectsArray
The error says the reason of crash. It is not allowed to change an array while enumerating.
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
In the above code you were enumerating the array an inserting and object. This is where the error will be given. Hope this helps .. :)

Thread-safe design with ARC

First of all let me quote a chapter from Apple Threading Programming Guide:
Be Aware of Threats to Code Correctness
When using locks and memory barriers, you should always give careful thought to their
placement in your code. Even locks that seem well placed can actually
lull you into a false sense of security. The following series of
examples attempt to illustrate this problem by pointing out the flaws
in seemingly innocuous code. The basic premise is that you have a
mutable array containing a set of immutable objects. Suppose you want
to invoke a method of the first object in the array. You might do so
using the following code:
NSLock* arrayLock = GetArrayLock();
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
[arrayLock unlock];
[anObject doSomething];
Because the array is mutable, the lock around the array prevents other
threads from modifying the array until you get the desired object. And
because the object you retrieve is itself immutable, a lock is not
needed around the call to the doSomething method.
There is a problem with the preceding example, though. What happens if
you release the lock and another thread comes in and removes all
objects from the array before you have a chance to execute the
doSomething method? In an application without garbage collection, the
object your code is holding could be released, leaving anObject
pointing to an invalid memory address. To fix the problem, you might
decide to simply rearrange your existing code and release the lock
after your call to doSomething, as shown here:
NSLock* arrayLock = GetArrayLock();
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
[anObject doSomething];
[arrayLock unlock];
By moving the doSomething call inside the lock, your code guarantees
that the object is still valid when the method is called.
Unfortunately, if the doSomething method takes a long time to execute,
this could cause your code to hold the lock for a long time, which
could create a performance bottleneck.
The problem with the code is not that the critical region was poorly
defined, but that the actual problem was not understood. The real
problem is a memory management issue that is triggered only by the
presence of other threads. Because it can be released by another
thread, a better solution would be to retain anObject before releasing
the lock. This solution addresses the real problem of the object being
released and does so without introducing a potential performance
penalty.
NSLock* arrayLock = GetArrayLock();
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
[anObject retain];
[arrayLock unlock];
[anObject doSomething];
[anObject release];
And the question is: Is there any way to solve the problem while using ARC?
ARC solves this problem for you automatically; by default every pointer is a strong pointer, which means that the object is guaranteed to be retained until you are done using that pointer.
This means that whenever you get an object out of an array, ARC always retains that object. This guarantees its lifetime, even if the object is later removed from the array.

Passing multiple parameters into function using array

I have to maintain a code of somebody. The code opens a thread. The thread worker function in iOS (and any other language I know of) accepts only one parameter. In order to overcome this, the code creates an array, adds all the parameters into an array and passes it into the thread. Here is the code.
NSArray* params = [[NSArray alloc] initWithObjects: mainView, actionFlag, nil];
[self performSelectorOnMainThread:#selector(updateWorker:) withObject:params waitUntilDone:NO];
And the function is called this way
-(void)updateWorker:(NSArray*)params
{
UIView* view = [params objectAtIndex:0];
bool actionFlag = ((NSNumber*)[params objectAtIndex:1]).boolValue;
/* do stuff with view and actionFlag */
}
I have a gut feeling that this is very wrong at so many levels but cannot built a valid argument for this case.
What are the drawbacks of passing number of arguments as an array?
Actually what you are doing is technically correct (but I do understand why it feels wrong).
If you want to feel better, what I would do in this case is instantiate (create) a "NSDictionary" object and then set the objects / values to useful keys and in your "updateWorker" method, fetch the objects via "objectForKey:".
Doing it this way will be easier for you (or somebody else) to maintain in the future, as you won't have to poke around to see what goes in array position 1, array position 2, etc.
Most of them are future updates,
Some cases (not so rare) may happen:
Addition of new parameters to the array
changing the order of elements in the array
removing elements in the array
problems when releasing and retaining elements in the array (not ARC)
One point to note here, is that all of these cases will be hard to debug, since you will be moving from one thread to the other

Resources