0

So I have a function that prepares a code block and schedules it to run synchronously on a serial dispatch queue. I'm aware that this serial queue will be run on a new thread. The problem, however, is that the code block modifies a variable that it communicates back to the function which in turn is expected to return it as its return value. The code below will help clarify the situation:

-(DCILocation*) getLocationsByIdentifier: (NSString*) identifier andQualifier: (NSString*) qualifier {

    __block DCILocation* retval = nil;

    NSString* queryStr = [self baseQueryWithFilterSet:nil];
    queryStr = [queryStr stringByAppendingString:@" (identifier = ? OR icao = ?) AND qualifier = ?"];

   [self.queue inDatabase:^(FMDatabase *db) {
        FMResultSet* results = [db executeQuery:queryStr,
                               [identifier uppercaseString],
                               [identifier uppercaseString],
                               [qualifier uppercaseString]];
        if ((nil != results) && [results next]) {
             dispatch_async(dispatch_get_main_queue(), ^{
                 retval = [DCIAirportEnumerator newAirportForStatement:results];
             });
            [results close];
        }
   }];

   return retval;
}

"self.queue" is the serial dispatch queue that the block will run on. Notice that the block modifies "retval" and updates it by nesting a dispatch_async call to the main thread. The concern however, is that "return retval" (the last line of the function) could be possibly called before the block of code running on the serial dispatch queue is able to modify it. This will result in "nil" being returned.

Any ideas as to how it can be made sure that the function doesn't return until retval as been modified by the block executing on the serial queue?

  • What's `self.queue`? Since we don't know your queue implementation we cannot help you with the serialization issue. – Nikolai Ruhe Jan 03 '13 at 15:50
  • 3
    You want an async solution to a sync problem. Ask yourself, do I really need to dispatch async since I have to wait for it anyway? – Rad'Val Jan 03 '13 at 15:58

2 Answers2

4

Any ideas as to how it can be made sure that the function doesn't return until retval as been modified by the block executing on the serial queue?

If you need to wait for the result, then your code is still synchronous and you might as well run it in your method instead of on a serial queue. So that's the first option: don't use blocks here.

The second option would be to restructure your code so that it really does run asynchronously. Find the code that depends on the return value retval and break that out into a separate method or block. Then have the block that sets retval call that passing in retval when it's finished.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • Thanks for the guidance, it makes a lot of sense. One more quick question. If, lets say in that block I call a method that's part of the same class that the block resides in using a '[self someMethod...]', will that work correctly? I'm concerned since that block will execute on a different thread and thus won't have access to external methods. – user1945991 Jan 04 '13 at 16:06
  • You can do that. See [Objective C - Calling \[self methodName\] from inside a block?](http://stackoverflow.com/q/5023566/643383) for a detailed answer. – Caleb Jan 04 '13 at 16:12
-2

After your block, you can add

while(YES) {
    if(variable) {
        break;
    }
}

And then add, in your dispatch_async, after you define retval

variable = YES;

You'll just have to define __block BOOL variable = NO; before the block.

Echihl
  • 346
  • 2
  • 9