you can't go home again

Debugging multithreading race conditions being high on my list of Things I Don't Enjoy, I created this category:

#import "NSRecursiveLock+Extras.h"

@implementation NSRecursiveLock (Extras)

- (void)executeBlock:(void(^)())block {
  [self lock];
  block();
  [self unlock];
}

@end

Now it's easy to protect critical code sections. Just take something like this:

self.foo = [self doFoo];  
self.didFoo = YES;  

and wrap it in a lock:

[self.lock executeBlock:^{
  self.foo = [self doFoo];
  self.didFoo = YES;
}];

What could possibly go wrong? Let's wrap another benign bit of code in a lock:

if (self.foo != nil) {  
  return;
}
self.foo = [self gimmeANewFoo];

[self doSomethingWithNewFoo];

which becomes:

[self.lock executeBlock:^{
  if (self.foo != nil) {
    return;
  }
  self.foo = [self gimmeANewFoo];
}];

[self doSomethingWithNewFoo];

The obvious-in-retrospect problem is that return in a block just exits the block. If the block is being queued for asychronous execution, that probably does what you want, to quit early.

If the block is being executed synchronously, return exits the block, but execution carries on after the block. In this case, [self doSomethingWithNewFoo] will now be executed all of the time. Which is decidedly not what I wanted.