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.