|
@@ -71,6 +71,8 @@ static NSString *const kGPBDataCoderKey = @"GPBData";
|
|
@package
|
|
@package
|
|
GPBUnknownFieldSet *unknownFields_;
|
|
GPBUnknownFieldSet *unknownFields_;
|
|
NSMutableDictionary *extensionMap_;
|
|
NSMutableDictionary *extensionMap_;
|
|
|
|
+ // Readonly access to autocreatedExtensionMap_ is protected via
|
|
|
|
+ // readOnlySemaphore_.
|
|
NSMutableDictionary *autocreatedExtensionMap_;
|
|
NSMutableDictionary *autocreatedExtensionMap_;
|
|
|
|
|
|
// If the object was autocreated, we remember the creator so that if we get
|
|
// If the object was autocreated, we remember the creator so that if we get
|
|
@@ -79,10 +81,10 @@ static NSString *const kGPBDataCoderKey = @"GPBData";
|
|
GPBFieldDescriptor *autocreatorField_;
|
|
GPBFieldDescriptor *autocreatorField_;
|
|
GPBExtensionDescriptor *autocreatorExtension_;
|
|
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.
|
|
|
|
|
|
+ // Message can only be mutated from one thread. But some *readonly* operations
|
|
|
|
+ // modifify internal state because they autocreate things. The
|
|
|
|
+ // autocreatedExtensionMap_ is one such structure. Access during readonly
|
|
|
|
+ // operations is protected via this semaphore.
|
|
// NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
|
|
// 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
|
|
// pointed out that they are vulnerable to live locking on iOS in cases of
|
|
// priority inversion:
|
|
// priority inversion:
|
|
@@ -583,19 +585,30 @@ static id GetOrCreateArrayIvarWithField(GPBMessage *self,
|
|
// This is like GPBGetObjectIvarWithField(), but for arrays, it should
|
|
// This is like GPBGetObjectIvarWithField(), but for arrays, it should
|
|
// only be used to wire the method into the class.
|
|
// only be used to wire the method into the class.
|
|
static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
|
|
static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
|
|
- id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
|
|
|
|
- if (!array) {
|
|
|
|
- // Check again after getting the lock.
|
|
|
|
- GPBPrepareReadOnlySemaphore(self);
|
|
|
|
- dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
|
|
|
|
- array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
|
|
|
|
- if (!array) {
|
|
|
|
- array = CreateArrayForField(field, self);
|
|
|
|
- GPBSetAutocreatedRetainedObjectIvarWithField(self, field, array);
|
|
|
|
- }
|
|
|
|
- dispatch_semaphore_signal(self->readOnlySemaphore_);
|
|
|
|
|
|
+ uint8_t *storage = (uint8_t *)self->messageStorage_;
|
|
|
|
+ _Atomic(id) *typePtr = (_Atomic(id) *)&storage[field->description_->offset];
|
|
|
|
+ id array = atomic_load(typePtr);
|
|
|
|
+ if (array) {
|
|
|
|
+ return array;
|
|
}
|
|
}
|
|
- return array;
|
|
|
|
|
|
+
|
|
|
|
+ id expected = nil;
|
|
|
|
+ id autocreated = CreateArrayForField(field, self);
|
|
|
|
+ if (atomic_compare_exchange_strong(typePtr, &expected, autocreated)) {
|
|
|
|
+ // Value was set, return it.
|
|
|
|
+ return autocreated;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Some other thread set it, release the one created and return what got set.
|
|
|
|
+ if (GPBFieldDataTypeIsObject(field)) {
|
|
|
|
+ GPBAutocreatedArray *autoArray = autocreated;
|
|
|
|
+ autoArray->_autocreator = nil;
|
|
|
|
+ } else {
|
|
|
|
+ GPBInt32Array *gpbArray = autocreated;
|
|
|
|
+ gpbArray->_autocreator = nil;
|
|
|
|
+ }
|
|
|
|
+ [autocreated release];
|
|
|
|
+ return expected;
|
|
}
|
|
}
|
|
|
|
|
|
static id GetOrCreateMapIvarWithField(GPBMessage *self,
|
|
static id GetOrCreateMapIvarWithField(GPBMessage *self,
|
|
@@ -613,19 +626,31 @@ static id GetOrCreateMapIvarWithField(GPBMessage *self,
|
|
// This is like GPBGetObjectIvarWithField(), but for maps, it should
|
|
// This is like GPBGetObjectIvarWithField(), but for maps, it should
|
|
// only be used to wire the method into the class.
|
|
// only be used to wire the method into the class.
|
|
static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
|
|
static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
|
|
- id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
|
|
|
|
- if (!dict) {
|
|
|
|
- // Check again after getting the lock.
|
|
|
|
- GPBPrepareReadOnlySemaphore(self);
|
|
|
|
- dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
|
|
|
|
- dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
|
|
|
|
- if (!dict) {
|
|
|
|
- dict = CreateMapForField(field, self);
|
|
|
|
- GPBSetAutocreatedRetainedObjectIvarWithField(self, field, dict);
|
|
|
|
- }
|
|
|
|
- dispatch_semaphore_signal(self->readOnlySemaphore_);
|
|
|
|
|
|
+ uint8_t *storage = (uint8_t *)self->messageStorage_;
|
|
|
|
+ _Atomic(id) *typePtr = (_Atomic(id) *)&storage[field->description_->offset];
|
|
|
|
+ id dict = atomic_load(typePtr);
|
|
|
|
+ if (dict) {
|
|
|
|
+ return dict;
|
|
}
|
|
}
|
|
- return dict;
|
|
|
|
|
|
+
|
|
|
|
+ id expected = nil;
|
|
|
|
+ id autocreated = CreateMapForField(field, self);
|
|
|
|
+ if (atomic_compare_exchange_strong(typePtr, &expected, autocreated)) {
|
|
|
|
+ // Value was set, return it.
|
|
|
|
+ return autocreated;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Some other thread set it, release the one created and return what got set.
|
|
|
|
+ if ((field.mapKeyDataType == GPBDataTypeString) &&
|
|
|
|
+ GPBFieldDataTypeIsObject(field)) {
|
|
|
|
+ GPBAutocreatedDictionary *autoDict = autocreated;
|
|
|
|
+ autoDict->_autocreator = nil;
|
|
|
|
+ } else {
|
|
|
|
+ GPBInt32Int32Dictionary *gpbDict = autocreated;
|
|
|
|
+ gpbDict->_autocreator = nil;
|
|
|
|
+ }
|
|
|
|
+ [autocreated release];
|
|
|
|
+ return expected;
|
|
}
|
|
}
|
|
|
|
|
|
#endif // !defined(__clang_analyzer__)
|
|
#endif // !defined(__clang_analyzer__)
|
|
@@ -3337,30 +3362,34 @@ id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field) {
|
|
|
|
|
|
id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
|
|
id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
|
|
NSCAssert(!GPBFieldIsMapOrArray(field), @"Shouldn't get here");
|
|
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)) {
|
|
if (!GPBFieldDataTypeIsMessage(field)) {
|
|
|
|
+ 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.
|
|
return field.defaultValue.valueMessage;
|
|
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;
|
|
|
|
|
|
+ uint8_t *storage = (uint8_t *)self->messageStorage_;
|
|
|
|
+ _Atomic(id) *typePtr = (_Atomic(id) *)&storage[field->description_->offset];
|
|
|
|
+ id msg = atomic_load(typePtr);
|
|
|
|
+ if (msg) {
|
|
|
|
+ return msg;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ id expected = nil;
|
|
|
|
+ id autocreated = GPBCreateMessageWithAutocreator(field.msgClass, self, field);
|
|
|
|
+ if (atomic_compare_exchange_strong(typePtr, &expected, autocreated)) {
|
|
|
|
+ // Value was set, return it.
|
|
|
|
+ return autocreated;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Some other thread set it, release the one created and return what got set.
|
|
|
|
+ GPBClearMessageAutocreator(autocreated);
|
|
|
|
+ [autocreated release];
|
|
|
|
+ return expected;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma clang diagnostic pop
|
|
#pragma clang diagnostic pop
|