Inferring QoS && Priority Inversion

1. In chapter 3 “Inferring QoS” part,

If you submit a task with a higher quality of service than the queue has, the queue’s level will increase. Not only that, but all the operations enqueued will also have their priority raised as well.

I was trying to test that situation by using the following code:


let queue = DispatchQueue(label: "label",
                          qos: .utility,
                          attributes: .concurrent)
queue.async(qos: .userInteractive) {
    print(queue.qos)
}

But it turns out:

**DispatchQoS(qosClass: Dispatch.DispatchQoS.QoSClass.utility, relativePriority: 0)**

The QoS is still .utility rather than .userInteractive.

2. In chapter 5, “Priority Inversion” part,

The more common situation wherein priority inversion occurs is when a higher quality of service queue shares a resource with a lower quality of service queue

the example code shows the case, but I got confused about common resources they are sharing.
They didn’t access anything except the print function. Wait, is the print function the common resource?

:point_down:The example code

high.async {
    // Wait 2 seconds just to be sure all the other tasks have enqueued
Thread.sleep(forTimeInterval: 2) semaphore.wait()
defer { semaphore.signal() }
    print("High priority task is now running")
}

for i in 1 ... 10 { medium.async {
let waitTime = Double(exactly: arc4random_uniform(7))! print("Running medium task \(i)") Thread.sleep(forTimeInterval: waitTime)
} }

low.async { semaphore.wait()
defer { semaphore.signal() }
    print("Running long, lowest priority task")
Thread.sleep(forTimeInterval: 5) }
1 Like

Apple determines when to change the priority. It won’t happen just because you do it once. There’s no clearly defined algorithm for when it decides to make that change. Best guess is that if you are abusing the queue, and other things are using a similar queue, it might decide to move it.

For your second question. There’s no specific element being shared here…semaphore is the shared resource control. The what you’re sharing iOS has no idea. But you control access to that resource via the semaphore.

So for this example you can think of the print statement as the common resource, sure.

I still don’t understand the example given in the book as I would like to. The author of the book says,

The more common situation wherein priority inversion occurs is when a higher quality of service queue shares a resource with a lower quality of service queue. When the lower queue gets a lock on the object, the higher queue now has to wait.

But I still don’t grasp the example fully.

let high = DispatchQueue.global(qos: .userInteractive)
let medium = DispatchQueue.global(qos: .userInitiated)
let low = DispatchQueue.global(qos: .background)

let semaphore = DispatchSemaphore(value: 1)

high.async {
    // Wait 2 seconds just to be sure all the other tasks have enqueued
    Thread.sleep(forTimeInterval: 2)
    semaphore.wait()
    defer { semaphore.signal() }

    print("High priority task is now running")
}

for i in 1 ... 10 {
    medium.async {
        let waitTime = Double(exactly: arc4random_uniform(7))!
        print("Running medium task \(i)")
        Thread.sleep(forTimeInterval: waitTime)
    }
}

low.async {
    semaphore.wait()
    defer { semaphore.signal() }

    print("Running long, lowest priority task")
    Thread.sleep(forTimeInterval: 5)
}

Could you please explain the priority inversion in the given example again, but in another way?