In Android, I could safely access and modify primitive types from different threads. I used this to share data between my OpenGL draw loop and user settings that were modified in the main thread Android UI. By storing each setting in a primitive type and making each independent of the others' values, it was thread-safe to modify all these variables without using locks or the synchronize keyword.
Is this also true in Objective-C? I read that placing atomic on a variable essentially causes the synthesized getter and setter to use locks, similar to using synchronized methods in Java. And I've read that the reason for this is so an object does not get partially modified while it's being read by another thread.
But are primitive types safe from being partially modified, as they are in Java? If that is the case, it seems like I could use my same old paradigm from Java for sharing data between threads. But then the atomic keyword would be pointless for a primitive, correct?
I also read that a more robust and faster solution than using atomic variables is to copy objects before using them if they are accessed from multiple threads. But I'm not sure how that could be accomplished. Couldn't a nonatomic object get modified while it's in the process of being copied, thereby corrupting the copy?
Primitive types aren't guaranteed to be safe from being partially modified because modifications to primitive types aren't guaranteed to be atomic in C and Objective-C inherits from there. C's only guarantees concern sequence points and there's no requirement that the processing between two sequence points be atomic — a rule of thumb is that each full expression is a sequence point.
In practice, modifying primitives is often a two-step process; the modification is made in a register and then written out to memory. It's very unlikely that the write itself will not be atomic but there's also no guarantee of when it will occur versus the modification. Even with the volatile qualification, the only guarantees provided are in terms of sequence points.
Apple exposes some C functions for atomic actions via OSAtomic.h that map directly to the specialised atomic instructions that CPUs offer for the implementation of concurrency mechanisms. It's possible you could use one of those more directly than via a heavy-handed mutex.
Common patterns in Objective-C are:
immutable objects and functional transformations — there are memory management reasons as well but that's partly why NSString, NSArray, etc, are specifically distinct from NSMutableString, NSMutableArray, etc;
serial dispatch queues, which can be combined with copy-modify-replace by copying on the queue, going off somewhere else to modify, then jumping back onto the queue to replace;
such #synchronizeds, NSConditionLocks or other explicit synchronisation mechanisms as are appropriate.
The main thread itself is a serial dispatch queue, which is why you can ignore the issue of concurrency entirely if you restrict yourself to it.
Atomic synthesized #properties are immune to concurrent partial updates. The accessor methods will use a lock if necessary on that architecture.
In general, primitive types in C are not necessarily safe with respect to concurrent partial updates.
I don't believe you can partially modify a primitive type, that's part of what makes them primitive. You either modify it or you don't. In that sense, I would say that they are thread safe.
You are correct when you say that the atomic keyword would be pointless for a primitive type.
Someone already took a stab at this here:
Objective-c properties for primitive types
Related
Question
I am working on a project where I am concerned about the thread safety of an object's properties. I know that when a property is an object such as an NSString, I can run into situations where multiple threads are reading and writing simultaneously. In this case you can get a corrupt read and the app will either crash or result in corrupted data.
My question is for primitive value type properties such as BOOLs or NSIntegers. I am wondering if I can get into a similar situation where I read a corrupt value when reading and writing from multiple threads (and the app will crash)? In either case, I am interested in why.
Clarification - 1/13/17
I am mostly interested in if a primitive value type property is differently susceptible to crashing due to multiple threads accessing it at the same time than an object such as NSMutableString, custom created object, etc. In addition, if there is a difference when accessing memory on the stack vs heap relative to multithreading.
Clarification - 12/1/17
Thank you to #Rob for pointing me to the answer here: stackoverflow.com/a/34386935/1271826! This answer has a great example that shows that depending on the type of architecture you are on (32-bit vs 64-bit), you can get an undefined result when using a primitive property.
Although this is a great step towards answering my question, I still wonder two things:
If there is a multithreading difference when accessing a primitive value property on the stack vs heap (as noted in my previous clarification)?
If you restrict a program to running on one architecture, can you still find yourself in an undefended state when access a primitive value property and why?
I should note that here has been a lot of conversation around atomic vs nonatomic in response to this question. Although this is generally an important concept, this question has little to do with preventing undefined multithreading behavior by using the atomic property modifier or any other thread safety approach such as using GCD.
If your primitive value type property is atomic, then you're assured it cannot be corrupted because your reading it from one thread while setting it from another (as long as you only use the accessor methods, and not interact with the backing ivar directly). That's the entire purpose of atomic. And, as you suggest, this only applicable to fundamental data types (or objects that are both immutable and stateless). But in these narrow cases, atomic can be useful.
Having said that, this is a far cry from concluding that the app is thread-safe. It only assures you that the access to that one property is thread-safe. But often thread-safety must be considered within a broader context. (I know you assure us that this is not the case here, but I qualify this for future readers who too quickly jump to the conclusion that atomic is sufficient to achieve thread-safety. It often is not.)
For example, if your NSInteger property is "how many items are in this cache object", then not only must that NSInteger have its access synchronized, but it must be also be synchronized in conjunction with all interactions with the cache object (e.g. the "add item to cache" and "remove item from cache" tasks, too). And, in these cases, since you'll synchronize all interaction with this broader object somehow (e.g. with GCD queue, locks, #synchronized directive, whatever), making the NSInteger property atomic then becomes redundant and therefore modestly less efficient.
Bottom line, in limited situations, atomic can provide thread-safety for fundamental data types, but frequently it is insufficient when viewed in a broader context.
You later say that you don't care about race conditions. For what it's worth, Apple argues that there is no such thing as a benign race. See WWDC 2016 video Thread Sanitizer and Static Analysis (about 14:40 into it).
Anyway, you suggest you are merely concerned whether the value can be corrupted or whether the app will crash:
I am wondering if I can get into a similar situation where I read a corrupt value when reading and writing from multiple threads (and the app will crash)?
The bottom line is that if you're reading from one thread while mutating on another, the behavior is simply undefined. It could vary. You are simply well advised to avoid this scenario.
In practice, it's a function of the target architecture. For example on 64-bit type (e.g. long long) on 32-bit x86 target, you can easily retrieve a corrupt value, where one half of the 64-bit value is set and the other is not. (See https://stackoverflow.com/a/34386935/1271826 for example.) This results in merely non-sensical, invalid numeric values when dealing with primitive types. For pointers to objects, this obviously would have catestrophic implications.
But even if you're in an environment where no problems are manifested, it's an incredibly fragile approach to eschew synchronization to achieve thread-safety. It could easily break when run on new, unanticipated hardware architectures or compiled under different configuration. I'd encourage you to watch that Thread Sanitizer and Static Analysis video for more information.
Let's say we have a NSMutableArray for example on a singleton object. The singleton object can obviously be called from multiple different threads.
Let's say we need users of the singleton to be able to addObjects or removeObjects. Or perhaps we have a custom object that we need to both set and read.
What's the proper way to handle these cases? Should every thread-unsafe property on a singleton have it's own serial/concurrent queue, and then overwrite addObject and removeObject functions for the NSMutableArray, wrapping reads in dispatch_sync, and writes in either dispatch_async(to a serial queue) or dispatch_barrier_async(to a concurrent queue)?
1) Does every thread-unsafe property need its own queue? Or should it at least have one in terms of performance. If multiple properties shared the same queue, it would be slower than necessary.
2) In what cases is this thread protection unnecessary. Ever? Or should thread-unsafe properties always have their setters and getter overwritten.
1) Does every thread-unsafe property need its own queue? Or should it
at least have one in terms of performance. If multiple properties
shared the same queue, it would be slower than necessary.
Depends entirely on how frequently you are pounding on the serialized APIs. Start with a simple model and then move to more complex if needed.
And keep in mind that the implementation isn't necessarily limited to just adding queues at whim.
You could also use a multiple-reader, one writer model. That would involve keeping an NSArray* copy of the mutable array's contents that the readers can simultaneously hit. If the mutable array's contents are changed, then you can (thread safety in mind) swap a new immutable copy of the mutable array for the existing NSArray * (could be done with an atomic #property or you could use a concurrent queue for all reads with a barrier for the swap operation).
Something like the above might make sense when either the array is relatively small (cheap copies) or there are many many times more reads than writes.
2) In what cases is this thread protection unnecessary. Ever? Or
should thread-unsafe properties always have their setters and getter
overwritten.
A mutable collection is always thread unsafe, even when operating in a "readonly" mode. Only immutable collections are thread safe.
So, yes, you need to make sure you serialize any operation that would cause a thread unsafe action.
So I was reading this article about an attempt to remove the global interpreter lock (GIL) from the Python interpreter to improve multithreading performance and saw something interesting.
It turns out that one of the places where removing the GIL actually made things worse was in memory management:
With free-threading, reference counting operations lose their thread-safety. Thus, the patch introduces a global reference-counting mutex lock along with atomic operations for updating the count. On Unix, locking is implemented using a standard pthread_mutex_t lock (wrapped inside a PyMutex structure) and the following functions...
...On Unix, it must be emphasized that simple reference count manipulation has been replaced by no fewer than three function calls, plus the overhead of the actual locking. It's far more expensive...
...Clearly fine-grained locking of reference counts is the major culprit behind the poor performance, but even if you take away the locking, the reference counting performance is still very sensitive to any kind of extra overhead (e.g., function call, etc.). In this case, the performance is still about twice as slow as Python with the GIL.
and later:
Reference counting is a really lousy memory-management technique for free-threading. This was already widely known, but the performance numbers put a more concrete figure on it. This will definitely be the most challenging issue for anyone attempting a GIL removal patch.
So the question is, if reference counting is so lousy for threading, how does Objective-C do it? I've written multithreaded Objective-C apps, and haven't noticed much of an overhead for memory management. Are they doing something else? Like some kind of per object lock instead of a global one? Is Objective-C's reference counting actually technically unsafe with threads? I'm not enough of a concurrency expert to really speculate much, but I'd be interested in knowing.
There is overhead and it can be significant in rare cases (like, for example, micro-benchmarks ;), regardless of the optimizations that are in place (of which, there are many). The normal case, though, is optimized for un-contended manipulation of the reference count for the object.
So the question is, if reference counting is so lousy for threading, how does Objective-C do it?
There are multiple locks in play and, effectively, a retain/release on any given object selects a random lock (but always the same lock) for that object. Thus, reducing lock contention while not requiring one lock per object.
(And what Catfish_man said; some classes will implement their own reference counting scheme to use class-specific locking primitives to avoid contention and/or optimize for their specific needs.)
The implementation details are more complex.
Is Objectice-C's reference counting actually technically unsafe with threads?
Nope -- it is safe in regards to threads.
In reality, typical code will call retain and release quite infrequently, compared to other operations. Thus, even if there were significant overhead on those code paths, it would be amortized across all the other operations in the app (where, say, pushing pixels to the screen is really expensive, by comparison).
If an object is shared across threads (bad idea, in general), then the locking overhead protecting the data access and manipulation will generally be vastly greater than the retain/release overhead because of the infrequency of retaining/releasing.
As far as Python's GIL overhead is concerned, I would bet that it has more to do with how often the reference count is incremented and decremented as a part of normal interpreter operations.
In addition to what bbum said, a lot of the most frequently thrown around objects in Cocoa override the normal reference counting mechanisms and store a refcount inline in the object, which they manipulate with atomic add and subtract instructions rather than locking.
(edit from the future: Objective-C now automatically does this optimization on modern Apple platforms, by mixing the refcount in with the 'isa' pointer)
In my few years as an iOS developer I don't think I've ever used atomic on a property. If I can see potential race conditions or data integrity issues due to threading, using atomic on a #property wouldn't ever help. I use conventional transaction/unit-of-work thread safety techniques (using mechanisms locks, semaphores or whatever).
Does anyone have (or know of) any practical examples of where atomic is used? (I'd love to see some actual/practical code examples)
After writing nonatomic for maybe the billionth time, I'm also wondering why Apple have decided to make atomic the default.
As for the first problem you're having, maybe it's because
Although “atomic” means that access to the property is thread-safe, simply making all the properties in your class atomic does not mean that your class or more generally your object graph is “thread safe”—thread safety cannot be expressed at the level of individual accessor methods.
As for why apple makes it atomic by default, I don't think there is any good reason, it was simply a bad design decision. Guys at the WWDC sessions repeatedly encouraged people to use nonatomic wherever you can to eliminate the performance impact.
It's worth noting that under garbage collection, almost all synthesized accessors are inherently atomic—there would be no difference between the atomic and nonatomic version, since the simple assignment of a pointer is all that's required in both cases. Since you really can't make a nonatomic synthesized accessor under garbage collection, they may have decided that it made more sense to just have things atomic by default.
I don't have any evidence that this was the reasoning behind the decision, but it makes sense to me.
(In case you're curious, there are still cases under garbage collection where simple assignment is nonatomic—this happens when the value is larger than the word size of the process. In practice, that only happens with structs.)
Edit: Added sources
More information on the atomicity of synthesized properties under garbage collection can be found in The Objective-C Programming Language -> Declared Properties -> Performance and Threading, where it says "In a garbage collected environment, most synthesized methods are atomic without incurring this overhead." The inherent atomicity is mentioned more explicitly in the 2008 WWDC session 420 "Using Garbage Collection with Objective-C", around the 29 minute mark.
In two words - thread safety. Most of the applications we write on a regular basis are fairly simple and as such are actually going to benefit from not having the additional locks.
From Apple's The Objective-C Programming Language:
Properties are atomic by default so that synthesized accessors provide robust access to properties in a multi-threaded environment—that is, the value returned from the getter or set via the setter is always fully retrieved or set regardless of what other threads are executing concurrently. For more details, see “Performance and Threading.”
If you do not specify nonatomic, then in a reference counted environment a synthesized get accessor for an object property uses a lock and retains and autoreleases the returned value—the implementation will be similar to the following:
[_internal lock]; // lock using an object-level lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;
If you specify nonatomic, then a synthesized accessor for an object property simply returns the value directly.
When Apple first introduced the concept of properties, there was a big argument about whether atomic or nonatomic should be the default and the atomic people won.
I think the reasoning is that in a threaded environment, unless the property is atomic, you can't guarantee that the pointer it returns is valid. A non atomic getter would be implemented something like this
-(MyObj*) someProperty
{
return someInstVar;
}
It's possible that some other thread could deallocate the object pointed to by someInstVar after the pointer has been placed in the register for returning but before the caller has had time to retain it. Even this is no good:
-(MyObj*) someProperty
{
return [[someInstVar retain] autorelease];
}
because some other thread could dealloc someInstVar just before the retain count is incremented.
The only way to safely return the pointer in a multithreaded environment is something like this:
-(MyObj*) someProperty
{
#synchronized(self)
{
return [[someInstVar retain] autorelease];
}
}
and also to synchronise the setter too.
However, the problem with that is that the lock is very expensive (in reality they used something much lighter than #synchronized but it's still expensive) so everybody was using nonatomic anyway and just making all the properties atomic doesn't confer thread safety in general so other techniques are required anyway and they tend to obviate the problem I discussed above.
There are plenty of people who think the wrong decision was made about what the default should be, but it can't be changed now for backwards compatibility. I find myself typing nonatomic without even thinking now.
Atomic calls are calls that cannot be interrupted. The entire call must be executed.
Updating something like a shared counter variable would be something that could be atomic because you wouldn't want two processes trying to access the variable at the same time. The actions would need to be executed "atomically".
There is a lot of useful information in this SO post: Atomic vs nonatomic properties as to what the differences are and the thread safety issues of atomic vs nonatomic.
The documentation for properties in Obj-C 2.0 say that atomic properties use a lock internally, but it doesn't document the specifics of the lock. Does anybody know if this is a per-property lock, a per-object lock separate from the implicit one used by #synchronized(self), or the equivalent of #synchronized(self)?
Looking at the generated code (iOS SDK GCC 4.0/4.2 for ARM),
32-bit assign properties (including struct {int32_t v;}) are accessed directly.
Larger-than-32-bit structs are accessed with objc_copyStruct().
double and int64_t are accessed with objc_copyStruct, except on GCC 4.0 where they're accessed directly with stmia/ldmia (I'm not sure if this is guaranteed to be atomic in case of interrupts).
retain/copy accessors call objc_getProperty and objc_setProperty.
Cocoa with Love: Memory and thread-safe custom property methods gives some details on how they're implemented in runtime version objc4-371.2; obviously the precise implementation can vary between runtimes (for example, on some platforms you can use atomic swap/CAS to spin on the ivar itself instead of using another lock).
The lock used by atomic #properties is an implementation detail--for appropriate types on appropriate platforms, atomic operations without a lock are possible and I'd be surprised if Apple was not taking advantage of them. There is no public access to the lock in any case, so you can't #synchronize on the same lock. Several Apple engineers have pointed out that atomic properties do not guarantee thread safety; atomic properties only guarantee that gets/sets of that value are atomic. For correct thread safety, you will have to make use of higher-level locking or synchronization and you almost certainly would not want to use the same lock as the synthesize getter/setter(s) might be using.