Parcourir la source

managers: avoid priority inversions

1. Stop using the default background queue because it is designed for actual
   background tasks.
2. Create named queues when applicable. This helps debugging.
3. VM operations, JSON parsing, and QMP handling are all the same priority
   now and therefore avoids priority inversion.

Fixes #4352
osy il y a 2 ans
Parent
commit
7073f0317a

+ 1 - 1
Managers/UTMJSONStream.m

@@ -145,7 +145,7 @@ enum ParserState {
     }
     NSAssert([json isKindOfClass:[NSDictionary class]], @"JSON data not dictionary");
     UTMLog(@"Debug JSON recieved <- %@", json);
-    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
+    dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
         [self.delegate jsonStream:self receivedDictionary:(NSDictionary *)json];
     });
 }

+ 9 - 1
Managers/UTMQemu.m

@@ -20,6 +20,12 @@
 #import <pthread.h>
 #import <TargetConditionals.h>
 
+@interface UTMQemu ()
+
+@property (nonatomic) dispatch_queue_t completionQueue;
+
+@end
+
 @implementation UTMQemu {
     NSMutableArray<NSString *> *_argv;
     NSMutableArray<NSURL *> *_urls;
@@ -54,6 +60,8 @@
         if (![self setupXpc]) {
             return nil;
         }
+        dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, QOS_MIN_RELATIVE_PRIORITY);
+        self.completionQueue = dispatch_queue_create("QEMU Completion Queue", attr);
     }
     return self;
 }
@@ -144,7 +152,7 @@
     pthread_attr_init(&qosAttribute);
     pthread_attr_set_qos_class_np(&qosAttribute, QOS_CLASS_USER_INTERACTIVE, 0);
     pthread_create(&qemu_thread, &qosAttribute, self.entry, (__bridge_retained void *)self);
-    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
+    dispatch_async(self.completionQueue, ^{
         if (dispatch_semaphore_wait(self.done, DISPATCH_TIME_FOREVER)) {
             dlclose(dlctx);
             completion(NO, NSLocalizedString(@"Internal error has occurred.", @"UTMQemu"));

+ 3 - 3
Managers/UTMQemuManager.m

@@ -254,7 +254,7 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
 }
 
 - (void)qmpPowerCommand:(NSString *)command completion:(void (^ _Nullable)(NSError * _Nullable))completion {
-    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
+    dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
         Error *qerr = NULL;
         NSError *err;
         NSDictionary *cmd = @{
@@ -272,7 +272,7 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
 }
 
 - (void)qmpHmpCommand:(NSString *)cmd completion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion {
-    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
+    dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
         Error *qerr = NULL;
         NSError *err;
         NSString *result;
@@ -323,7 +323,7 @@ void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void
 }
 
 - (void)mouseIndexForAbsolute:(BOOL)absolute withCompletion:(void (^)(int64_t, NSError * _Nullable))completion {
-    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
+    dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
         MouseInfoList *info = NULL;
         Error *qerr = NULL;
         int64_t index = -1;

+ 2 - 1
Managers/UTMQemuVirtualMachine.m

@@ -74,7 +74,8 @@ NSString *const kSuspendSnapshotName = @"suspend";
         self.qemuWillQuitEvent = dispatch_semaphore_create(0);
         self.qemuDidExitEvent = dispatch_semaphore_create(0);
         self.qemuDidConnectEvent = dispatch_semaphore_create(0);
-        self.vmOperations = dispatch_queue_create("com.utmapp.UTM.VMOperations", DISPATCH_QUEUE_SERIAL);
+        dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
+        self.vmOperations = dispatch_queue_create("com.utmapp.UTM.VMOperations", attr);
     }
     return self;
 }

+ 1 - 1
Platform/iOS/VMSessionState.swift

@@ -39,7 +39,7 @@ import SwiftUI
     #if !WITH_QEMU_TCI
     private var primaryUsbManager: CSUSBManager?
     
-    private var usbManagerQueue = DispatchQueue(label: "USB Manager Queue", qos: .background)
+    private var usbManagerQueue = DispatchQueue(label: "USB Manager Queue", qos: .utility)
     
     @Published var mostRecentConnectedDevice: CSUSBDevice?
     

+ 3 - 3
Platform/macOS/Display/VMDisplayQemuDisplayController.swift

@@ -363,7 +363,7 @@ extension VMDisplayQemuWindowController: CSUSBManagerDelegate {
             guard response == .alertFirstButtonReturn else {
                 return
             }
-            DispatchQueue.global(qos: .background).async {
+            DispatchQueue.global(qos: .utility).async {
                 usbManager.connectUsbDevice(usbDevice) { (result, message) in
                     DispatchQueue.main.async {
                         if let msg = message {
@@ -445,7 +445,7 @@ extension VMDisplayQemuWindowController {
             return
         }
         let device = allUsbDevices[menu.tag]
-        DispatchQueue.global(qos: .background).async {
+        DispatchQueue.global(qos: .utility).async {
             usbManager.connectUsbDevice(device) { (result, message) in
                 DispatchQueue.main.async {
                     if let msg = message {
@@ -469,7 +469,7 @@ extension VMDisplayQemuWindowController {
             return
         }
         let device = allUsbDevices[menu.tag]
-        DispatchQueue.global(qos: .background).async {
+        DispatchQueue.global(qos: .utility).async {
             usbManager.disconnectUsbDevice(device) { (result, message) in
                 DispatchQueue.main.async {
                     if let msg = message {

+ 2 - 6
Platform/macOS/Display/VMDisplayWindowController.swift

@@ -82,9 +82,7 @@ class VMDisplayWindowController: NSWindowController {
     
     @IBAction func restartButtonPressed(_ sender: Any) {
         showConfirmAlert(NSLocalizedString("This will reset the VM and any unsaved state will be lost.", comment: "VMDisplayWindowController")) {
-            DispatchQueue.global(qos: .background).async {
-                self.vm.requestVmReset()
-            }
+            self.vm.requestVmReset()
         }
     }
     
@@ -245,9 +243,7 @@ extension VMDisplayWindowController: NSWindowDelegate {
     
     func windowWillClose(_ notification: Notification) {
         if !isSecondary {
-            DispatchQueue.global(qos: .background).async {
-                self.vm.requestVmStop(force: true)
-            }
+            self.vm.requestVmStop(force: true)
         }
         secondaryWindows.forEach { secondaryWindow in
             secondaryWindow.close()