Lena Bru
Lena Bru

Reputation: 13947

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 ?

Upvotes: 1

Views: 126

Answers (2)

stefreak
stefreak

Reputation: 1450

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;

}

Upvotes: 2

gnasher729
gnasher729

Reputation: 52632

If you have five tasks running in parallel which try to change the some dictionary then a crash is to be expected. Changing responseDict must be done using @synchronized. NSMutableDictionary is not thread safe.

Upvotes: 2

Related Questions