Can NSOperation have a lower qualityOfService than NSOperationQueue? - ios

The doc of NSOperationQueue.qualityOfService says:
This property specifies the service level applied to operation objects
added to the queue. If the operation object has an explicit service
level set, that value is used instead.
It doesn't mention anything about the relative quality between operation and queue so any combination should work.
However, GCD has some corresponding features whose docs say:
DISPATCH_BLOCK_ENFORCE_QOS_CLASS
Flag indicating that execution of a dispatch block object submitted to
a queue should prefer the QOS class assigned to the block (resp.
associated with the block at the time of submission) over the QOS
class assigned to the queue, as long as doing so will not result in a
lower QOS class.
If NSOperation and NSOperationQueue are built on top of GCD — which I believe is true — it seems the restriction should also apply to them.
So if I add an NSOperation with a lower qualityOfService to an NSOperationQueue will it run with its own lower qualityOfService or the queue's higher qualityOfService?

Thanks #das for pointing me to Foundation Release Notes for OS X v10.10.
Ignoring the operation dependency part, rules of QoS between NSOperation and NSOperationQueue are exactly the same as that between dispatch block and dispatch queue — though #das points out "the QoS implementation of NSOperationQueue is not built on top of the GCD QoS block primitives" the rules are actually the same, that is to say, NSOperation's QoS(if lower) will be raise to NSOperationQueue's QoS. So my initial guess is correct. The doc of NSOperationQueue.qualityOfService is wrong:
If the operation object has an explicit service level set, that value
is used instead.
Here are the detailed explanation of QoS rules of NSOperation and NSOperationQueue:
NSOperationQueue qualityOfService
NSOperationQueue has a new qualityOfService property.
You can change the qualityOfService property at any time.
When an operation is added to a queue, the queue's qualityOfService value at that time may affect the effective QOS that the operation will be run at:
If the queue property has not been set, the operation is unaffected.
If the queue property has been set to NSQualityOfServiceDefault, the operation is unaffected.
If the queue property is set to another value, the operation is promoted to the queue's qualityOfService if its promotion QOS is not already at least that level.
If the qualityOfService property of a queue is changed while there are operations in the queue, the effective QOS of operations in the queue will be affected, just as the operations were when added to the queue. Thus, when the qualityOfService property of a queue is changed, all operations in the queue, running or not, have their effective QOS raised up to that level (if they were lower), and future additions to the queue are raised to that level (when they are lower). If the qualityOfService property is lowered from one level to another, only future additions will be affected by that new lower value. Operations' promotion or effective QOS is never lowered by the setting of, or the set value of, a queue's qualityOfService.
When an operation is added to a queue, the operation's effective QOS values at that time may affect the effective QOS of the operations which are already in the queue ("ahead of it"):
The operations already ahead in the queue of the newly added operation, running or not, are promoted to the effective QOS of the operation being added.
Thus, if a high QOS operation is added to a queue, operations already in the queue are raised up to that level (if they were lower). Operations added after that high-QOS operation are not affected by its presence in the queue.
The meaning and interaction of operation promotion QOS and effective QOS is discussed in the section on NSOperation qualityOfService.
NSOperationQueues do not infer any QOS from any execution context.
If the (dispatch_queue_t) underlyingQueue property of an NSOperationQueue is set, qualityOfService property values of NSOperationQueues and NSOperations have no effect. The effective QOS of operations run by that queue is determined by the state of the dispatch_queue_t.
NSOperation qualityOfService
NSOperation has a new qualityOfService property.
You can change the qualityOfService property at any time.
There are various real and virtual QOS values that relate to how an operation runs:
The qualityOfService property value
An inferred QOS
The promotion QOSes
The effective QOS
When an operation object is created, an inferred QOS value is computed from the execution context:
If either:
the operation is being created in the execution context of another operation (already running on that thread); or
the operation is being created in the execution context of a certain NSProcessInfo API; then the nearest one of those to the current activation frame of the call stack of the current thread is used as the inferred QOS of the new operation:
that operation's effective QOS at the time it started running;
the NSProcessInfo API's values are mapped to a QOS value.
If the operation is being created on the main thread, the inferred QOS is NSQualityOfServiceUserInitiated.
Otherwise, the current thread's QOS (which may be none) is read and used as the inferred QOS of the new operation.
An operation can be promoted (have promotion QOSes applied to it) in several contexts:
When the operation is added to a queue, or when the qualityOfService property of the queue the operation is in is changed
(as discussed in the NSOperationQueue section)
When a different operation is added to a queue that the operation (in
question) is already in
(as discussed in the NSOperationQueue section)
When a different later (after the operation in question) operation in the same queue has its effective QOS raised
the effective QOS of the other operation promotes the operation
When a different operation (a dependee) becomes dependent upon the operation in question
the effective QOS of the dependee promotes the operation
When a dependee operation has its effective QOS raised
the new effective QOS of the dependee promotes the operation
When the operation is waited upon, with the -waitUntilFinished method, or indirectly when the operation's queue's -waitUntilAllOperationsAreFinished method, is used
if the waiting thread is the main thread, the promotion QOS is taken to be NSQualityOfServiceUserInteractive;
otherwise if the waiting is done in the execution context of another operation, its effective QOS promotes the operation;
otherwise the QOS of the current thread promotes the operation.
These are all collectively called the promotion QOSes; or for the MAX() of all of them, just promotion QOS.
These various values are put together into the effective QOS. The effective QOS is the MAX() of all of these QOS values: {the inferred QOS, the promotion QOSes, the qualityOfService property value}, with these qualifications:
If the operation's qualityOfService property has been explicitly set to anything, even NSQualityOfServiceDefault, the inferred QOS is ignored.
If the operation's qualityOfService property has not been explicitly set to anything, it is ignored (as if no value exists).
All QOS values of NSQualityOfServiceDefault are ignored.
If there are no QOS values after all that ignoring, the effective QOS is NSQualityOfServiceDefault.
Thus, for example, if an operation is waited upon, its effective QOS may be raised by the waiting context, which may recursively raised all of its dependent operations and all operations ahead of it in the queue (and so on recursively outward in the tree of these relationships).
An operation's qualityOfService property value has no effect if the operation is started manually, rather than being put in an NSOperationQueue, unless the code which is starting it reads out that value and makes some appropriate use of it; that is outside the purview of Foundation.

The QoS implementation of NSOperationQueue is not built on top of the GCD QoS block primitives.
The NSOperationQueue behaviors are documented in detail in the Foundation release notes.
For GCD queues, the only time QoS is lowered when executing a block with assigned or propagated QoS is when the queue the block was submitted to has unspecified QoS (queue was created without a QoS attribute and is not targeting a non-default global queue). In that case, the queue is initially serviced by a worker thread at QoS class default and lowering is allowed.
See the documentation in the dispatch/block.h header or the GCD WWDC sessions from 2015 and 2014 for more details.

spindump's timeline mode will show you the QoS of the sampled process's threads. You might have to contrive things a little to get spindump to tell you what's going on, and give you some visibility into how the QoS promotion stuff works. The idea of QoS boosting is to prevent priority inversions, so a user-initiated operation won't get starved out.

Related

Which is thread Safe atomic or non-atomic?

After reading this answer I am very confused.
Some says atomic is thread safe and some are saying nonatomic is thread safe.
What is the exact answer of this.
Thread unsafety of operations is caused by the fact that an operation can be divided into several suboperations, for example:
a = a + 1
can be subdivided into operations
load value of a
add 1 to the loaded value
assign the calculated value to a.
The word "Atomic" comes from "atom" which comes from greek "atomos", which means "that which can't be split". For an operation it means that it is always performed as a whole, it is never performed one suboperation at a time. That's why it is thread safe.
TL;DR Atomic = thread safe.
Big warning: Having properties atomic does not mean that a whole function/class is thread safe. Atomic properties means only that operations with the given properties are thread safe.
Obviously, nonatomic certainly isn’t thread safe. More interestingly, atomic is closer, but it is insufficient to achieve “thread safety”, as well. To quote Apple’s Programming with Objective-C: Encapsulating Data:
Note: Property atomicity is not synonymous with an object’s thread safety.
It goes on to provide an example:
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.
This example is quite simple, but the problem of thread safety becomes much more complex when considered across a network of related objects. Thread safety is covered in more detail in Concurrency Programming Guide.
Also see bbum’s Objective-C: Atomic, properties, threading and/or custom setter/getter.
The reason this is so confusing is that, in fact, the atomic keyword does ensure that your access to that immediate reference is thread safe. Unfortunately, when dealing with objects, that’s rarely sufficient. First, you have no assurances that the property’s own internal properties are thread safe. Second, it doesn’t synchronize your app’s access to the object’s individual properties (such as Apple’s example above). So, atomic is almost always insufficient to achieve thread safety, so you generally have to employ some higher-level degree of synchronization. And if you provide that higher-level synchronization, adding atomicity to that mix is redundant and inefficient.
So, with objects, atomic rarely has any utility. It can be useful, though, when dealing primitive C data types (e.g. integers, booleans, floats). For example, you might have some boolean that might be updated in some other thread indicating whether that thread’s asynchronous task is completed. This is a perfect use case for atomic.
Otherwise, we generally reach for higher-level synchronization mechanisms for thread safety, such as GCD serial queues or reader-writer pattern (... or, less common nowadays, locks, the #synchronized directive, etc.).
As you can read at developer.apple you should use atomic functions for thread savety.
You can read more about atomic functions here: Atomic Man page
In short:
Atomic ~ not splittable ~ not shared by threads
As is mentioned in several answers to the posted question, atomic is thread safe. This means that getter/setter working on any thread should finish first, before any other thread can perform getter/setter.

Do I have to read NSOperationQueue's operationCount on the operation queue itself?

Sometimes it seems operationCount does not return the right value. Do I have to access it from the queue itself or it does not matter if I access it from an other thread?
"the value returned by this property reflects the instantaneous number of operations at the time the property was accessed."
it isn't guaranteed to be precise / stable and you should NOT use it to do calculations / decisions

NSOperation - queuePriority

What does queuepriority mean. According to what i understand, it decides the execution within the operation queue. But, when i use a queue with only 1 concurrent operation, the queue priority does not make any effect. It executes the operation in the same order in which it is added.
The queuePriority property might have no effect, as you said yourself, in cases where a queue is serial only in which case operations are executed in the order they arrive. This is not 100% accurate because the operations will attempt to be launched in the order of priorities but I'm guessing that in the case you describe the operations are getting enqueued slower than they are being executed. Consider this example of a completely legal execution sequence:
If Op1 already executes by the time Op2 is enqueued, Op1 will not be interrupted in favor of Op2, even if Op2's priority is higher.

Mutex are needed to protect the Condition Variables

As it is said that Mutex are needed to protect the Condition Variables.
Is the reference here to the actual condition variable declared as pthread_cond_t
OR
A normal shared variable count whose values decide the signaling and wait.
?
is the reference here to the actual condition variable declared as pthread_cond_t or a normal shared variable count whose values decide the signaling and wait?
The reference is to both.
The mutex makes it so, that the shared variable (count in your question) can be checked, and if the value of that variable doesn't meet the desired condition, the wait that is performed inside pthread_cond_wait() will occur atomically with respect to that check.
The problem being solved with the mutex is that you have two separate operations that need to be atomic:
check the condition of count
wait inside of pthread_cond_wait() if the condition isn't met yet.
A pthread_cond_signal() doesn't 'persist' - if there are no threads waiting on the pthread_cond_t object, a signal does nothing. So if there wasn't a mutex making the two operations listed above atomic with respect to one another, you could find yourself in the following situation:
Thread A wants to do something once count is non-zero
Thread B will signal when it increments count (which will set count to something other than zero)
thread "A" checks count and finds that it's zero
before "A" gets to call pthread_cond_wait(), thread "B" comes along and increments count to 1 and calls pthread_cond_signal(). That call actually does nothing of consequence since "A" isn't waiting on the pthread_cond_t object yet.
"A" calls pthread_cond_wait(), but since condition variable signals aren't remembered, it will block at this point and wait for the signal that has already come and gone.
The mutex (as long as all threads are following the rules) makes it so that item #2 cannot occur between items 1 and 3. The only way that thread "B" will get a chance to increment count is either before A looks at count or after "A" is already waiting for the signal.
A condition variable must always be associated with a mutex, to avoid the race condition where a thread prepares to wait on a condition variable and another thread signals the condition just before the first thread actually waits on it.
More info here
Some Sample:
Thread 1 (Waits for the condition)
pthread_mutex_lock(cond_mutex);
while(i<5)
{
pthread_cond_wait(cond, cond_mutex);
}
pthread_mutex_unlock(cond_mutex);
Thread 2 (Signals the condition)
pthread_mutex_lock(cond_mutex);
i++;
if(i>=5)
{
pthread_cond_signal(cond);
}
pthread_mutex_unlock(cond_mutex);
As you can see in the same above, the mutex protects the variable 'i' which is the cause of the condition. When we see that the condition is not met, we go into a condition wait, which implicitly releases the mutex and thereby allowing the thread doing the signalling to acquire the mutex and work on 'i' and avoid race condition.
Now, as per your question, if the signalling thread signals first, it should have acquired the mutex before doing so, else the first thread might simply check the condition and see that it is not being met and might go for condition wait and since the second thread has already signalled it, no one will signal it there after and the first thread will keep waiting forever.So, in this sense, the mutex is for both the condition & the conditional variable.
Per the pthreads docs the reason that the mutex was not separated is that there is a significant performance improvement by combining them and they expect that because of common race conditions if you don't use a mutex, it's almost always going to be done anyway.
https://linux.die.net/man/3/pthread_cond_wait​
Features of Mutexes and Condition Variables
It had been suggested that the mutex acquisition and release be
decoupled from condition wait. This was rejected because it is the
combined nature of the operation that, in fact, facilitates realtime
implementations. Those implementations can atomically move a
high-priority thread between the condition variable and the mutex in a
manner that is transparent to the caller. This can prevent extra
context switches and provide more deterministic acquisition of a mutex
when the waiting thread is signaled. Thus, fairness and priority
issues can be dealt with directly by the scheduling discipline.
Furthermore, the current condition wait operation matches existing
practice.
I thought that a better use-case might help better explain conditional variables and their associated mutex.
I use posix conditional variables to implement what is called a Barrier Sync. Basically, I use it in an app where I have 15 (data plane) threads that all do the same thing, and I want them all to wait until all data planes have completed their initialization. Once they have all finished their (internal) data plane initialization, then they can start processing data.
Here is the code. Notice I copied the algorithm from Boost since I couldnt use templates in this particular application:
void LinuxPlatformManager::barrierSync()
{
// Algorithm taken from boost::barrier
// In the class constructor, the variables are initialized as follows:
// barrierGeneration_ = 0;
// barrierCounter_ = numCores_; // numCores_ is 15
// barrierThreshold_ = numCores_;
// Locking the mutex here synchronizes all condVar logic manipulation
// from this point until the point where either pthread_cond_wait() or
// pthread_cond_broadcast() is called below
pthread_mutex_lock(&barrierMutex_);
int gen = barrierGeneration_;
if(--barrierCounter_ == 0)
{
// The last thread to call barrierSync() enters here,
// meaning they have all called barrierSync()
barrierGeneration_++;
barrierCounter_ = barrierThreshold_;
// broadcast is the same as signal, but it signals ALL waiting threads
pthread_cond_broadcast(&barrierCond_);
}
while(gen == barrierGeneration_)
{
// All but the last thread to call this method enter here
// This call is blocking, not on the mutex, but on the condVar
// this call actually releases the mutex
pthread_cond_wait(&barrierCond_, &barrierMutex_);
}
pthread_mutex_unlock(&barrierMutex_);
}
Notice that every thread that enters the barrierSync() method locks the mutex, which makes everything between the mutex lock and the call to either pthread_cond_wait() or pthread_mutex_unlock() atomic. Also notice that the mutex is released/unlocked in pthread_cond_wait() as mentioned here. In this link it also mentions that the behavior is undefined if you call pthread_cond_wait() without having first locked the mutex.
If pthread_cond_wait() did not release the mutex lock, then all threads would block on the call to pthread_mutex_lock() at the beginning of the barrierSync() method, and it wouldnt be possible to decrease the barrierCounter_ variables (nor manipulate related vars) atomically (nor in a thread safe manner) to know how many threads have called barrierSync()
So to summarize all of this, the mutex associated with the Conditional Variable is not used to protect the Conditional Variable itself, but rather it is used to make the logic associated with the condition (barrierCounter_, etc) atomic and thread-safe. When the threads block waiting for the condition to become true, they are actually blocking on the Conditional Variable, not on the associated mutex. And a call to pthread_cond_broadcast/signal() will unblock them.
Here is another resource related to pthread_cond_broadcast() and pthread_cond_signal() for an additional reference.

Is dispatch_sync(dispatch_get_global_queue(xxx), task) sync or async

As Apple's document says, dispatch_get_global_queue() is a concurrent queue, and dispatch_sync is something meaning serial.Then the tasks are processed async or sync?
You're getting confused between what a queue is and what async vs sync means.
A queue is an entity on which blocks can be run. These can be serial or concurrent. Serial means that if you put block on in the order A, B, C, D, then they will be executed A, then B, then C, then D. Concurrent means that these same blocks might be executed in a different order and possibly even more than one at the same time (assuming you have multiple cores on which to run, obviously).
Then onto async vs sync. Async means that when you call dispatch_async, it will return immediately and the block will be queued on the queue. Sync means that when you call dispatch_sync it will return only after the block has finished executing.
So to fully answer your question, if you dispatch_sync onto a global concurrent queue then this block will be run, perhaps in parallel with other blocks on that queue, but in a synchronous manner - i.e. it won't return until the block is finished.
Apple Doc says
dispatch_get_global_queue
Returns a well-known global concurrent queue of a given priority
level.
dispatch_queue_t dispatch_get_global_queue(
long priority,
unsigned long flags);
Parameters
priority The priority of the queue being retrieved. For a
list of possible values, see “dispatch_queue_priority_t”. flags This
value is reserved for future use. You should always pass 0. Return
Value Returns the requested global queue.
Discussion
The well-known global concurrent queues cannot be modified.
Calls to dispatch_suspend, dispatch_resume, dispatch_set_context, and
the like have no effect when used with queues returned by this
function.
Blocks submitted to these global concurrent queues may be executed
concurrently with respect to each other.
Availability Available in iOS 4.0 and later. Declared In
dispatch/queue.h
In the Discussion they have said that-> 'blocks submitted MAY be executed concurrently wrt each other.'
So Tasks may be processed Async with each other.

Resources