@ -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
/ / Message can only be mutated from one thread . But some * readonly * operations
/ / by * read * operations such as getters ( autocreation of message fields and
/ / modifify internal state because they autocreate things . The
/ / message extensions , not setting of values ) . Used to guarantee thread safet y
/ / autocreatedExtensionMap_ is one such structure . Access during readonl y
/ / for concurrent reads on the messag e.
/ / operations is protected via this semaphor e.
/ / 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 ) ;
uint8_t * storage = ( uint8_t * ) self - > messageStorage_ ;
if ( !array ) {
_Atomic ( id ) * typePtr = ( _Atomic ( id ) * ) & storage [ field - > description_ - > offset ] ;
/ / Check again after getting the lock .
id array = atomic_load ( typePtr ) ;
GPBPrepareReadOnlySemaphore ( self ) ;
if ( array ) {
dispatch_semaphore_wait ( self - > readOnlySemaphore_ , DISPATCH_TIME_FOREVER ) ;
return array ;
array = GPBGetObjectIvarWithFieldNoAutocreate ( self , field ) ;
if ( !array ) {
array = CreateArrayForField ( field , self ) ;
GPBSetAutocreatedRetainedObjectIvarWithField ( self , field , array ) ;
}
dispatch_semaphore_signal ( self - > readOnlySemaphore_ ) ;
}
}
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 ) ;
uint8_t * storage = ( uint8_t * ) self - > messageStorage_ ;
if ( !dict ) {
_Atomic ( id ) * typePtr = ( _Atomic ( id ) * ) & storage [ field - > description_ - > offset ] ;
/ / Check again after getting the lock .
id dict = atomic_load ( typePtr ) ;
GPBPrepareReadOnlySemaphore ( self ) ;
if ( dict ) {
dispatch_semaphore_wait ( self - > readOnlySemaphore_ , DISPATCH_TIME_FOREVER ) ;
return dict ;
dict = GPBGetObjectIvarWithFieldNoAutocreate ( self , field ) ;
if ( !dict ) {
dict = CreateMapForField ( field , self ) ;
GPBSetAutocreatedRetainedObjectIvarWithField ( self , field , dict ) ;
}
dispatch_semaphore_signal ( self - > readOnlySemaphore_ ) ;
}
}
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 ) ;
uint8_t * storage = ( uint8_t * ) self - > messageStorage_ ;
dispatch_semaphore_wait ( self - > readOnlySemaphore_ , DISPATCH_TIME_FOREVER ) ;
_Atomic ( id ) * typePtr = ( _Atomic ( id ) * ) & storage [ field - > description_ - > offset ] ;
GPBMessage * result = GPBGetObjectIvarWithFieldNoAutocreate ( self , field ) ;
id msg = atomic_load ( typePtr ) ;
if ( !result ) {
if ( msg ) {
/ / For non repeated messages , create the object , set it and return it .
return msg ;
/ / 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 ) ;
id expected = nil ;
GPBSetAutocreatedRetainedObjectIvarWithField ( self , field , result ) ;
id autocreated = GPBCreateMessageWithAutocreator ( field . msgClass , self , field ) ;
}
if ( atomic_compare_exchange_strong ( typePtr , & expected , autocreated ) ) {
dispatch_semaphore_signal ( self - > readOnlySemaphore_ ) ;
/ / Value was set , return it .
return result ;
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