-
-
Save janodev/4739849 to your computer and use it in GitHub Desktop.
dispatch_once is not reentrant. Use a NSReentrantLock if you want reentrancy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CC=clang | |
CFLAGS+=-std=c99 -g | |
LDFLAGS+=-framework Foundation | |
dispatch_once_reentrancy_test: dispatch_once_reentrancy_test.o |
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
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).