Search code examples
iosnsoperationqueue

Getting BAD_EXEC using NSOperationQueue


I need to run asynchronous tasks in my app

I have the following code:

- (NSDictionary *)parallelSendSync:(NSDictionary *)requests {

    NSMutableDictionary *responseDict = [[NSMutableDictionary alloc] init];
    for (NSString *key in [requests allKeys]) {
        [_parallelSendQueue addOperationWithBlock:^{
            NSDictionary *sendResult = [self send:requests[key] :nil];
           [responseDict setObject:sendResult forKey:key]; //this line sometimes throws BAD_EXEC
         }];

    }
     [_parallelSendQueue waitUntilAllOperationsAreFinished];

    return responseDict.copy;

}

_parallelSendQueue accepts max 5 concurrent operations

unfortunately this works only part of the time, sometimes it works OK, and sometimes it throws BAD_EXEC

what could be the reason for the bad exec ?


Solution

  • The problem is that multiple threads are using the same object which can lead to memory corruption for non-threadsafe objects.

    You have two options:

    • Lock the object you are using from multiple threads or a parallel queue so only one operation can change it at a time
    • Dispatch to one specific thread or serial queue that owns the shared object and change it from there (but beware, if you dispatch to the same thread that is currently calling waitUntilAllOperationsAreFinished the program is deadlocked)

    I think the best solution in your case is locking:

    - (NSDictionary *)parallelSendSync:(NSDictionary *)requests {
    
        NSMutableDictionary *responseDict = [[NSMutableDictionary alloc] init];
        for (NSString *key in [requests allKeys]) {
            [_parallelSendQueue addOperationWithBlock:^{
                NSDictionary *sendResult = [self send:requests[key] :nil];
                // synchronized locks the object so there is no memory corruption
                @synchronized(responseDict) {
                  [responseDict setObject:sendResult forKey:key];
                }
             }];
        }
         [_parallelSendQueue waitUntilAllOperationsAreFinished];
    
        return responseDict.copy;
    
    }