Explore Priority Inversion | raywenderlich.com

This is a companion discussion topic for the original entry at https://www.raywenderlich.com/9461083-ios-concurrency-with-gcd-and-operations/lessons/17

Hey Audrey! Enjoying the course so far and really learning a lot. I just have a question however. At time stamp 4:44 of video “Explore Priority Inversion” you talk about dispatching a task from the main queue to the utility custom queue. You’re saying the default QoS of the task being dispatched is .userInitiated. Why is this? Is the QoS set? or is there some sort of default implicit inference going on? I though QoS inference had to do with the queues. IE, when you initialize a custom queue and don’t specify its QoS argument, it will infer it from its current context EX. If initialized in the main queue, the custom queue will have a QoS of userInitiated… I feel like I’m missing something. How does the task being scheduled to the custom utility queue get a default QoS of userInitialized? Thanks! Looking forward to your response!

1 Like

Hey @audrey ! So, I’ve been doing some digging and reading online trying to see if I can get any more info on understanding the QoS inference. I found this…

“Queues that don’t have a QoS assigned and don’t target a global concurrent queue infer a QoS class of unspecified.”
Source: Energy Efficiency Guide for iOS Apps: Prioritize Work with Quality of Service Classes

So, It seems that my understanding wasn’t correct at all. It looks like when I create a custom/private queue and don’t specify a QoS argument it will actually get .unspecified QoS.

Now, this leads me back to my original question of… How does the task being scheduled to the custom utility queue get a default QoS of userInitialized?

I was under the impression that if you dispatched work/task to a custom queue (a queue that has a QoS specified) it will run at that QoS/Priority. I don’t understand how dispatching a task from the main queue onto a custom queue gives the task a default QoS of userInitiated as described from the lecture video at timestamp 4:45.

1 Like

hi Jason! Apple is paranoid about two things: Even when you’ve avoided deadlocks and data races, you can still get thread explosion and priority inversion/starvation, where a task never completes because it keeps being suspended because it has low priority or can’t access resources.

For thread explosion, so far all they can do is encourage developers to use the global queues as much as possible. For priority inversion, they have that complex system of inference and promotion that you’ve linked to.

In Table 4-2, Unspecified is described as “This represents the absence of QoS information and cues the system that an environmental QoS should be inferred.” — that is, the inference rules kick in.

The new Swift concurrency aims to solve these problems with a new way of switching between tasks — see Swift concurrency: Behind the scenes.

You can get some Swift concurrency background in async/await in SwiftUI and our Swift concurrency screencasts.

The default .userInitiated qos from main goes back to a 2015 WWDC video: Building Responsive and Efficient Apps with GCD, at about 13 minutes:

So first example is the example that Anthony had earlier where we performed some asynchronous work and do some I/O on the GCD cue off of the main thread.

How does this example fit in with QoS, what are the appropriate Quality of Service classes to apply here? On the left-hand side we have the main thread, of course. As Anthony mentioned, this is where the UI rendering happens, this is where the event handling happens, the appropriate caller service call here is user interactive. Nothing you have to do to get this. The main thread of the application comes up at this Quality of Service class. On the right-hand side of the screen, that’s the asynchronous work not happening on the main thread. Obviously, we shouldn’t be running user interactive. We’re not doing the UI rendering here. But, say the user tapped on the document icon and is waiting for his document to open. The user is blocked in his progress with the app. He can still interact with the UI but can’t do what he wants, which is edit the document or view the document. User initiated is the appropriate Quality of Service Class here.

So how do we achieve that with the GCD API. It turns out you don’t have to do anything, it will work automatically. But it’s important to understand why that is. Let’s look at that in detail. Everything starts with this initial dispatch async that works off the asynchronous work from the main thread. As I mentioned in the previous slide, dispatch async does automatic propagation of Quality of Service from the submitting thread to the queue where you submit the block to execute. Now, in this case there’s a special rule that applies which is that we automatically translate Quality of Service Class user interactive to user initiated. We do this so we don’t accidentaly over-propagate the Quality of Service Class that should be restricted to the main thread and UI rendering. We can take advantage of that here, because that is exactly what we want. That is typically the case by having the automatic propagation run the block on the queue at Quality of Service Class user initiated. Of course when you go back to the main thread to update the UI with the results this automatic propagation will also try to take place. Because the user main thread is Quality of Service Class user interactive, that will take priority. It will not lower if you go to a thread that has some assigned Quality of Service Class like the main thread. Here we will ignore this propagated value and run the UI update block at the bottom at Quality of Service Class user interactive. So we call this QoS propagation property inferred QoS. And this is QoS captured at the time the block gets submitted to a queue and with the special rule that we translate user interactive to user initiated as mentioned.

This propagated Quality of Service is used if the destination where the block is submitted to does not have its own quality of service specified and does not lower QoS if you go to the main thread that has its own high Quality of Service assigned.

1 Like