|
@@ -184,11 +184,10 @@ static id ExtensionForName(id self, SEL _cmd) {
|
|
|
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);
|
|
|
- }
|
|
|
+ // We can't remove the key from the dictionary here (as an optimization),
|
|
|
+ // two threads could have gone into +resolveClassMethod: for the same method,
|
|
|
+ // and ended up here; there's no way to ensure both return YES without letting
|
|
|
+ // both try to wire in the method.
|
|
|
dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
|
|
|
return extension;
|
|
|
}
|
|
@@ -212,9 +211,17 @@ BOOL GPBResolveExtensionClassMethod(Class self, SEL sel) {
|
|
|
#pragma unused(obj)
|
|
|
return extension;
|
|
|
});
|
|
|
- if (class_addMethod(metaClass, sel, imp, encoding)) {
|
|
|
- return YES;
|
|
|
+ BOOL methodAdded = class_addMethod(metaClass, sel, imp, encoding);
|
|
|
+ // class_addMethod() is documented as also failing if the method was already
|
|
|
+ // added; so we check if the method is already there and return success so
|
|
|
+ // the method dispatch will still happen. Why would it already be added?
|
|
|
+ // Two threads could cause the same method to be bound at the same time,
|
|
|
+ // but only one will actually bind it; the other still needs to return true
|
|
|
+ // so things will dispatch.
|
|
|
+ if (!methodAdded) {
|
|
|
+ methodAdded = GPBClassHasSel(metaClass, sel);
|
|
|
}
|
|
|
+ return methodAdded;
|
|
|
}
|
|
|
return NO;
|
|
|
}
|