aboutsummaryrefslogtreecommitdiffhomepage
path: root/objectivec/GPBMessage.m
diff options
context:
space:
mode:
Diffstat (limited to 'objectivec/GPBMessage.m')
-rw-r--r--objectivec/GPBMessage.m53
1 files changed, 51 insertions, 2 deletions
diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m
index cc1a650a..db5d3b60 100644
--- a/objectivec/GPBMessage.m
+++ b/objectivec/GPBMessage.m
@@ -32,6 +32,7 @@
#import <objc/runtime.h>
#import <objc/message.h>
+#import <stdatomic.h>
#import "GPBArray_PackagePrivate.h"
#import "GPBCodedInputStream_PackagePrivate.h"
@@ -77,6 +78,20 @@ static NSString *const kGPBDataCoderKey = @"GPBData";
GPBMessage *autocreator_;
GPBFieldDescriptor *autocreatorField_;
GPBExtensionDescriptor *autocreatorExtension_;
+
+ // A lock to provide mutual exclusion from internal data that can be modified
+ // by *read* operations such as getters (autocreation of message fields and
+ // message extensions, not setting of values). Used to guarantee thread safety
+ // for concurrent reads on the message.
+ // 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
+ // Use of readOnlySemaphore_ must be prefaced by a call to
+ // GPBPrepareReadOnlySemaphore to ensure it has been created. This allows
+ // readOnlySemaphore_ to be only created when actually needed.
+ _Atomic(dispatch_semaphore_t) readOnlySemaphore_;
}
@end
@@ -742,16 +757,22 @@ void GPBClearMessageAutocreator(GPBMessage *self) {
void GPBPrepareReadOnlySemaphore(GPBMessage *self) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdirect-ivar-access"
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Create the semaphore on demand (rather than init) as developers might not cause them
// to be needed, and the heap usage can add up. The atomic swap is used to avoid needing
// another lock around creating it.
if (self->readOnlySemaphore_ == nil) {
dispatch_semaphore_t worker = dispatch_semaphore_create(1);
- if (!OSAtomicCompareAndSwapPtrBarrier(NULL, worker, (void * volatile *)&(self->readOnlySemaphore_))) {
+ dispatch_semaphore_t expected = nil;
+ if (!atomic_compare_exchange_strong(&self->readOnlySemaphore_, &expected, worker)) {
dispatch_release(worker);
}
+#if defined(__clang_analyzer__)
+ // The Xcode 9.2 (and 9.3 beta) static analyzer thinks worker is leaked
+ // (doesn't seem to know about atomic_compare_exchange_strong); so just
+ // for the analyzer, let it think worker is also released in this case.
+ else { dispatch_release(worker); }
+#endif
}
#pragma clang diagnostic pop
@@ -3265,4 +3286,32 @@ id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field) {
return GetOrCreateMapIvarWithField(self, field, syntax);
}
+id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+ NSCAssert(!GPBFieldIsMapOrArray(field), @"Shouldn't get here");
+ if (GPBGetHasIvarField(self, field)) {
+ uint8_t *storage = (uint8_t *)self->messageStorage_;
+ id *typePtr = (id *)&storage[field->description_->offset];
+ return *typePtr;
+ }
+ // Not set...
+
+ // Non messages (string/data), get their default.
+ if (!GPBFieldDataTypeIsMessage(field)) {
+ return field.defaultValue.valueMessage;
+ }
+
+ GPBPrepareReadOnlySemaphore(self);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
+ GPBMessage *result = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+ if (!result) {
+ // For non repeated messages, create the object, set it and return it.
+ // This object will not initially be visible via GPBGetHasIvar, so
+ // we save its creator so it can become visible if it's mutated later.
+ result = GPBCreateMessageWithAutocreator(field.msgClass, self, field);
+ GPBSetAutocreatedRetainedObjectIvarWithField(self, field, result);
+ }
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
+ return result;
+}
+
#pragma clang diagnostic pop