34

In most of the discussions I've read, it indicates that making a property atomic does not guarantee it to be thread-safe, it just guarantees that the value returned won't be garbage as a result of one object writing to it and another trying to read it at the same time.

I understand this isn't thread-safe as a third object could be writing it and while the object accessing it wouldn't get garbage back, it's not entirely certain which value it will get back as multiple objects are writing to it at the same time, and it may get any of their values.

So when we say it won't return garbage, would garbage be in the sense that if an object was non-atomic and an object tried to access it while another was writing to it, it might get the result back mid-write, and only get a partial, incomplete version of the change brought about by the write? Is this what "garbage" means in this sense, and what atomic properties help to prevent?

Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • 8
    The thing about "thread safe" is that nothing, by itself, is "thread safe". "Thread safe" can only really be determined in a given context where "thread safe" means that correct operation of the entire context is assured in spite of any possible multi-threading. A particular implementation of a mutable array, say, can maybe said to be "thread safe" in a vacuum, since one will never access internally inconsistent data, but that's meaningless if it's entries are not maintained in synchronization with the external data it references. And that synchronization can only be maintained externally. – Hot Licks Jan 13 '14 at 18:40
  • 1
    @doug-smith You'll likely find my answer on this question + the comments the followed to be helpful. http://stackoverflow.com/questions/588866/atomic-vs-nonatomic-properties – bbum Jan 13 '14 at 20:28
  • Here is answer http://stackoverflow.com/a/32942413/1961064 – Grigori Jlavyan Feb 18 '16 at 13:45

5 Answers5

45

An atomic property in Objective C guarantees that you will never see partial writes. When a @property has the attribute atomic it is impossible to only partially write the value. The setter is like that:

- (void)setProp:(NSString *)newValue {
    [_prop lock];
    _prop = newValue;
    [_prop unlock];
}

So if two threads want to write the value @"test" and @"otherTest" at the same time, then at any given time the property can only be the initial value of the property or @"test" or @"otherTest". nonatomic is faster but the value is a garbage value and no partial String of @"test"/@"otherTest" (thx @Gavin) or any other garbage value.

But atomic is only thread-safe with simple use. It is not garantueed. Appledoc says the following:

Consider an XYZPerson object in which both a person’s first and last names are changed using atomic accessors from one thread. If another thread accesses both names at the same time, the atomic getter methods will return complete strings (without crashing), but there’s no guarantee that those values will be the right names relative to each other. If the first name is accessed before the change, but the last name is accessed after the change, you’ll end up with an inconsistent, mismatched pair of names.

I never had a problem using atomic at all. I designed the code that way, that there is not problem with atomic properties.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
Binarian
  • 12,296
  • 8
  • 53
  • 84
  • 11
    You would never see something like "teerTest", because setting the property to a string is just setting a pointer to an Objective-C object. So what you would maybe see is a garbage pointer, but never some hybrid of the two strings that you're trying to set (unless it happened to be a string defined somewhere else, like you actually had a string "teerTest" somewhere in your app, and the garbage pointer actually managed to point to it). – Gavin Jan 13 '14 at 18:47
  • 1
    Yeah, you are right. I wrote 'to make it clear', but it is more confusing that way. I changed it, thanks for mentioning it. – Binarian Jan 13 '14 at 18:50
  • 2
    What i understand from Apple document is that: if u have two atomic properties and u need to generate result data from these two while threads accessing or modifying them in any way then u will get result(guaranteed) but may not be correct,,which i feel is good enough instead of writing thread-safe code manually. – SandyNegi.037 Apr 10 '14 at 18:45
  • 2
    For me [this article about atomic and nonatomic operations at the processor level](http://preshing.com/20130618/atomic-vs-non-atomic-operations/) was particularly interesting to understand **how** a value might be written partially. – Yevhen Dubinin Mar 25 '16 at 17:46
  • I think you saying " you will see no partial writes" is misleading as if non-atomic would provide such ( You originally thought such and fixed only where the issue was bold, but didn't fix the other less important parts) `:]`. With respect I think your whole answer needs to be reconstructed again from scratch. Also I think your answer is a bit misleading in the sense that you have refuted partial written scenario, while that is a possibility if there is one writing attempt that has gone through half-way and a read is going on. ( this case is different from the case of 2 simultaneous attempts) – mfaani Apr 18 '16 at 13:47
  • @Gavin I am not sure, but are you not mixing two scenarios: scenario1: writing 2 values at the same time **and** scenario2: 'reading from a nonatomic while a value is being set halfway'--which can create partial reads? – mfaani Apr 18 '16 at 13:50
  • @Gavin, what if you use primitives as properties? In that case you could see some kind of hybrid objects from different threads writting different bits positions. – Pablo A. Jun 28 '16 at 21:24
7

In answer to your third paragraph; essentially yes. An atomic number can't be read while a thread is writing the number.

For example, if a thread has written the first two bytes of an atomic four byte number, and a read of that number is requested on another thread, that read has to wait until all four bytes have been written.

Conversely, if a thread has written the first two bytes of a non-atomic four byte number, and a read of that number is requested on another thread at that moment, it will read the first two new data bytes, but will get old data from a previous write operation in the other two bytes.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • Is it true conversely? If 4 bytes are not declared non-atomic, may one thread see 2 written bytes while another thread is writing 4? – danh Jan 13 '14 at 18:29
  • @danh: Too many double negatives for me to parse your question. I've edited my answer to clarify. – Robert Harvey Jan 13 '14 at 18:30
  • :-) I can't think of another way of phrasing not nonatomic. – danh Jan 13 '14 at 18:32
  • 2
    Exactly which types are naturally atomic is platform specific. In the case of x86, at least, 4 byte integers should be. structs or doubles would not be though. – Catfish_Man Jan 13 '14 at 18:37
  • Note that there are even problems with a single-byte value. In the processor, the code a++ reads the value from memory, adds 1 to it, and writes it back to memory (read/modify/write). Without the atomic qualifier or some other form of locking, it's possible that another thread could write a value to the location of "a" between the read and write, and that value would be lost. These sorts of bugs are very hard to find as they are largely non-determininsitic in nature. The atomic qualifier is not a panacea. You really need to understand concurrency. – Duncan C Jan 13 '14 at 18:47
  • Is your answer against iGodric's answer? OR he is talking about **2** values being written *simultaneously* and you are talking about getting a value while only **1** new value is being written? – mfaani Apr 18 '16 at 13:40
  • Fun bit of trivia; on ppc64, 128bit parameters could be split such that half was on the stack and half in a register at call sites. This led to some very hard to debug race conditions as it isn't all parameters that are split in this fashion and it depended entirely on what types preceded the split parameter. (There was a version of OS X that shipped where an `atomic` `@property` was not atomic in very specific cases.) – bbum Apr 18 '16 at 23:12
  • "For example, if a thread has written the first two bytes of an atomic four byte number, and a read of that number is requested on another thread, that read **has to wait** until all four bytes have been written." This is against bbums's answer from [here](https://stackoverflow.com/a/589392/5175709) "That is, if thread A is in the middle of the getter while thread B calls the setter, an actual viable value -- **an autoreleased object, most likely** -- will be returned to the caller in A." ie bbum says you don't wait and just get an AR value, you say you do wait till the write is done!? @bbum. – mfaani Nov 12 '17 at 10:04
  • @Honey If the value is atomic, then operations on the value are done serially (sometimes all operations, sometimes writing blocks on reads while also blocking new reads). Same thing for object values. But because the object value is typically backed by malloc()'d memory, then the atomic read case implies a `retain/autorelease` of the returned object to prevent a subsequent write from making the object prematurely invalid. – bbum Nov 13 '17 at 17:28
  • @bbum I'm not sure I understand you. Does this mean it would wait till the next write is finished and then read? Or it would attempt to read any completely written value, even if it comes from autorelease pool? – mfaani Nov 13 '17 at 18:15
  • @Honey For an `atomic` object property, a read will always wait for a write **and** it will always return an object that has been `retain` and `autorelease` (putting aside ARC optimizations for the moment). So, it behaves just like an `int`, *but* because it is backed by a heap allocation, the retain/autorelease is also required to ensure that the atomic read doesn't return an object whose lifespan is arbitrarily cut short by a subsequent write. – bbum Nov 13 '17 at 18:22
4

Robert Harvey's answer is correct, but there is a sub-case of that to consider that people often miss. Consider this code:

#import <Foundation/Foundation.h>

@interface Test : NSObject
@property (readwrite, strong) NSMutableArray *atomicArray;
@property (nonatomic, readwrite, strong) NSMutableArray *nonatomicArray;
@end

@implementation Test
@end

int main() {
    @autoreleasepool {
        Test *t = [[Test alloc] init];

        NSMutableArray *a = [[NSMutableArray alloc] init];
        [t setAtomicArray:a];
        [a release];

        NSMutableArray *one = [t atomicArray];
        [t setAtomicArray: nil];
        [one addObject:@"Test"];

        a = [[NSMutableArray alloc] init];
        [t setNonatomicArray:a];
        [a release];

        NSMutableArray *two = [t nonatomicArray];
        [t setNonatomicArray:nil];
        [two addObject:@"Test"];
    }
}

As well as preventing you from reading partially written values, atomic properties also prevent you from getting objects back that you don't control the lifetime of (they do this by retaining and then autoreleasing the object). This is important in single threaded code like the example that I linked, but even more important in multithreaded code where another thread could cause the object to be released out from under you.

Willeke
  • 14,578
  • 4
  • 19
  • 47
Catfish_Man
  • 41,261
  • 11
  • 67
  • 84
  • Can you please give an example for an iOS app, where thread safety is used. Let's say I am using Instagram and I attempt to download my images asynchronously, what problem could I have? It's just one device, one user, one main thread commanding everything else, **which exact** *hypothetical* **property** is it that could get wrote twice *simultaneously* and would need me to follow a thread safe design? – mfaani Apr 18 '16 at 13:33
  • 1
    If you're downloading them asynchronously, the main thread isn't commanding everything. Most likely what you would run into in the situation you describe is not several simultaneous writes, but a simultaneous read (from the main thread) and write (from the download thread). Atomic properties are likely not the tool you'd use to achieve thread-safety in that example though; there are many other approaches. – Catfish_Man Apr 18 '16 at 15:05
  • Where is this documented that the caller gets an autoreleased object back from an atomic getter so that the result can't be released by another thread? – Trygve Jul 16 '22 at 16:26
0

In concurrent programing:

atomic means if a property value being accessed for writing operation in some thread(thread # 1) and other thread(thread # 2) tries to access the atomic value either for read or write operation then other thread(thread # 2) waits until thread # 1 completes its task. In other words atomic synchronize the access of property on first come first serve basis.

non atomic means if a property value being accessed for writing operation in some thread(thread # 1) and other thread(thread # 2) tries to access the non atomic value either for read or write operation then other thread(thread # 2) gets value immediately gets old value

Shashi3456643
  • 2,021
  • 17
  • 21
0

Explicit implementation of

@property (atomic, retain) NSNumber *count

would be like this

- (NSNumber *)count {
    NSNumber *count;
    @synchronized(self) {
        count = [_count retain]; // +1
    }
    return [count autorelease]; // delayed -1
}

- (void)setCount:(NSNumber *)count {
    id oldValue;
    @synchronized(self) {
        oldValue = _count;
        _count = [count retain];
    }
    [oldValue release];
}

Atomic is the default behaviour for a property.An atomic property adds a level of thread safety when getting or setting values. That is, the getter and setter for the property will always be fully completed regardless of what other threads are doing. these properties will be a little slower to access than a nonatomic equivalent.

And explicitly we would implement

@property (nonatomic, retain) NSNumber *count

like this

- (NSNumber *)count {
    return _count;
}

- (void)setCount:(NSNumber *)count {
    if (count != _count) {
        id oldValue = _count;
        _count = [count retain];
        [_oldValue release];
    }
}

Nonatomic properties are not thread safe, and will return their properties directly. This will be faster than atomic properties, but obviously carries some risk if precautions aren’t made.

setter & getter for these Nonatomic property

Suraj K Thomas
  • 5,773
  • 4
  • 52
  • 64