Skip to content

Instantly share code, notes, and snippets.

@janodev
Forked from boredzo/Makefile
Last active Sep 23, 2016
Embed
What would you like to do?
dispatch_once is not reentrant. Use a NSReentrantLock if you want reentrancy.
#import <Foundation/Foundation.h>
static NSRecursiveLock *_lock = nil;
// constructor = run before main. used = emit code even if the function is not referenced.
// See http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
static void runBeforeMain(void) __attribute__ ((constructor, used));
static void runBeforeMain(void) {
_lock = [NSRecursiveLock new];
}
static void test(void)
{
static NSUInteger count = 0;
NSLog(@"iteration #%lu", ++count);
// YES = deadlock after a reentrant call using dispatch_once
// NO = allow reentrant calls using NSRecursiveLock
BOOL deadlock = YES;
if (deadlock){
static dispatch_once_t token;
dispatch_once(&token, ^{
test();
});
} else {
[_lock lock];
test();
[_lock unlock];
}
count--;
}
int main(int argc, char **argv) {
@autoreleasepool {
test();
}
return EXIT_SUCCESS;
}
CC=clang
CFLAGS+=-std=c99 -g
LDFLAGS+=-framework Foundation
dispatch_once_reentrancy_test: dispatch_once_reentrancy_test.o
@janodev
Copy link
Author

janodev commented Feb 8, 2013

dispatch_once is a barrier: it blocks other threads until it is done executing. However, it is not reentrant: it is not safe to call it again from the same thread before the previous invocation is done executing.

Reentrant calls with dispatch_once cause a deadlock. If you want a lock with reentrancy use NSRecursiveLock.

Note that dispatch_once is popular because it is faster than @synchronized to run a task just once (eg: initialize a singleton). But this is not true for all uses: @synchronized is faster than dispatch_once to execute repeated checks for race conditions (eg: atomic properties).

@lemonkey
Copy link

lemonkey commented Sep 23, 2016

Does this address the issue with using @synchronized(self) within a singleton while code elsewhere could also being using @synchronized(singleton_instance), causing a dead lock?

While dispatch_once doesn't have this problem, like you mention it is not reentrant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment