aboutsummaryrefslogtreecommitdiff
path: root/objectivec/GPBRootObject.m
diff options
context:
space:
mode:
authorFeng Xiao <xfxyjwf@gmail.com>2015-12-21 00:31:04 -0800
committerFeng Xiao <xfxyjwf@gmail.com>2015-12-21 00:34:19 -0800
commitd21780841734326d8a7653710f4f23b5366c4447 (patch)
tree25df44708c81059a3e1c7025e12b734cc80ee3f4 /objectivec/GPBRootObject.m
parent1e2fece3e01b8e079b4f6930306377a9332a2ddb (diff)
parentb27f2893b2a118b4e3bfcd23d32114dbdd4e6d9b (diff)
downloadprotobuf-d21780841734326d8a7653710f4f23b5366c4447.tar.gz
protobuf-d21780841734326d8a7653710f4f23b5366c4447.tar.bz2
protobuf-d21780841734326d8a7653710f4f23b5366c4447.zip
Merge branch master into v3.0.0-beta-2
Diffstat (limited to 'objectivec/GPBRootObject.m')
-rw-r--r--objectivec/GPBRootObject.m28
1 files changed, 22 insertions, 6 deletions
diff --git a/objectivec/GPBRootObject.m b/objectivec/GPBRootObject.m
index 7036723f..4570716f 100644
--- a/objectivec/GPBRootObject.m
+++ b/objectivec/GPBRootObject.m
@@ -31,7 +31,6 @@
#import "GPBRootObject_PackagePrivate.h"
#import <objc/runtime.h>
-#import <libkern/OSAtomic.h>
#import <CoreFoundation/CoreFoundation.h>
@@ -96,13 +95,19 @@ static CFHashCode GPBRootExtensionKeyHash(const void *value) {
return jenkins_one_at_a_time_hash(key);
}
-static OSSpinLock gExtensionSingletonDictionaryLock_ = OS_SPINLOCK_INIT;
+// NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+// pointed out that they are vulnerable to live locking on iOS in cases of
+// priority inversion:
+// http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+// https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+static dispatch_semaphore_t gExtensionSingletonDictionarySemaphore;
static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
+ (void)initialize {
// Ensure the global is started up.
if (!gExtensionSingletonDictionary) {
+ gExtensionSingletonDictionarySemaphore = dispatch_semaphore_create(1);
CFDictionaryKeyCallBacks keyCallBacks = {
// See description above for reason for using custom dictionary.
0,
@@ -134,9 +139,10 @@ static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
+ (void)globallyRegisterExtension:(GPBExtensionDescriptor *)field {
const char *key = [field singletonNameC];
- OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+ DISPATCH_TIME_FOREVER);
CFDictionarySetValue(gExtensionSingletonDictionary, key, field);
- OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
}
static id ExtensionForName(id self, SEL _cmd) {
@@ -166,14 +172,24 @@ static id ExtensionForName(id self, SEL _cmd) {
key[classNameLen] = '_';
memcpy(&key[classNameLen + 1], selName, selNameLen);
key[classNameLen + 1 + selNameLen] = '\0';
- OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
+
+ // NOTE: Even though this method is called from another C function,
+ // gExtensionSingletonDictionarySemaphore and gExtensionSingletonDictionary
+ // will always be initialized. This is because this call flow is just to
+ // lookup the Extension, meaning the code is calling an Extension class
+ // message on a Message or Root class. This guarantees that the class was
+ // initialized and Message classes ensure their Root was also initialized.
+ NSAssert(gExtensionSingletonDictionary, @"Startup order broken!");
+
+ dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+ DISPATCH_TIME_FOREVER);
id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
if (extension) {
// The method is getting wired in to the class, so no need to keep it in
// the dictionary.
CFDictionaryRemoveValue(gExtensionSingletonDictionary, key);
}
- OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
return extension;
}