All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
@ 2018-11-11 21:24 Berkus Decker
  2018-11-20 12:59 ` Peter Maydell
  0 siblings, 1 reply; 13+ messages in thread
From: Berkus Decker @ 2018-11-11 21:24 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 17280 bytes --]

It seems that Cocoa checks are stricter on Mojave and some callbacks that worked from non-GUI thread on High Sierra are no longer working.

The fixes included here are:

* Deferring qemu_main() to another thread so that the actual main thread is reserved for the Cocoa UI; it also removes blocking from applicationDidFinishLoading: delegate. I beleive this alone caused complete UI blockage on Mojave.
* Deferring UI-related updates in callbacks to the UI thread using invokeOnMainThread helper function. This function uses DDInvocationGrabber object courtesy of Dave Dribin, licensed under MIT license.
Here’s relevant blog post for his code: https://www.dribin.org/dave/blog/archives/2008/05/22/invoke_on_main_thread/

NSInvocation is used here instead of plain performSelectorOnMainThread:withObject:waitUntilDone: because we want to be able to pass non-id types to the handlers.

These changes are ought to work on OSX 10.6, although I don’t have a machine handy to test it.

Fixes https://bugs.launchpad.net/qemu/+bug/1802684

From 8f86e30f3710d782d78dccdbe7a1564ae79220c7 Mon Sep 17 00:00:00 2001
From: Berkus Decker <berkus+github@metta.systems>
Date: Sun, 11 Nov 2018 21:58:17 +0200
Subject: [PATCH 1/2] ui/cocoa: Defer qemu to another thread, leaving main
 thread for the UI

This prevents blocking in applicationDidFinishLoading: which is
not recommended and now causes complete UI lock on macOS Mojave.

Signed-off-by: Berkus Decker <berkus+github@metta.systems>
---
 ui/cocoa.m | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/ui/cocoa.m b/ui/cocoa.m
index ecf12bfc2e..f69f7105f2 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -1089,9 +1089,13 @@ QemuCocoaView *cocoaView;
 {
     COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
 
-    int status;
-    status = qemu_main(argc, argv, *_NSGetEnviron());
-    exit(status);
+    dispatch_queue_t qemu_runner = dispatch_queue_create("qemu-runner", DISPATCH_QUEUE_SERIAL);
+
+    dispatch_async(qemu_runner, ^{
+        int status;
+        status = qemu_main(argc, argv, *_NSGetEnviron());
+        exit(status);
+    });
 }
 
 /* We abstract the method called by the Enter Fullscreen menu item
-- 
2.18.0


From 467b0f67d94616ef98d2ec1e8d16eeb5e9506b4e Mon Sep 17 00:00:00 2001
From: Berkus Decker <berkus+github@metta.systems>
Date: Sun, 11 Nov 2018 20:22:01 +0200
Subject: [PATCH 2/2] ui/cocoa: Fix UI crashes on macOS Mojave

Signed-off-by: Berkus Decker <berkus+github@metta.systems>
---
 ui/DDInvocationGrabber.h | 124 ++++++++++++++++++++++++++++
 ui/DDInvocationGrabber.m | 171 +++++++++++++++++++++++++++++++++++++++
 ui/Makefile.objs         |   2 +-
 ui/cocoa.m               |  57 ++++++++-----
 4 files changed, 333 insertions(+), 21 deletions(-)
 create mode 100644 ui/DDInvocationGrabber.h
 create mode 100644 ui/DDInvocationGrabber.m

diff --git a/ui/DDInvocationGrabber.h b/ui/DDInvocationGrabber.h
new file mode 100644
index 0000000000..7218421d74
--- /dev/null
+++ b/ui/DDInvocationGrabber.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2007-2008 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+/*
+ *  This class is based on CInvocationGrabber:
+ *
+ *  Copyright (c) 2007, Toxic Software
+ *  All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of the Toxic Software nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+/**
+ * @class DDInvocationGrabber
+ * @discussion DDInvocationGrabber is a helper object that makes it very easy
+ * to construct instances of NSInvocation for later use. The object is
+ * inspired by NSUndoManager's prepareWithInvocationTarget method. To use
+ * a DDInvocationGrabber object, you set its target to some object, then send
+ * it a message as if it were the target object (the DDInvocationGrabber object
+ * acts as a proxy), if the target message understands the message the
+ * DDInvocationGrabber object stores the message invocation.
+
+ DDInvocationGrabber *theGrabber = [DDInvocationGrabber invocationGrabber];
+ [theGrabber setTarget:someObject]
+ // Send messages to 'theGrabber' as if it were 'someObject'
+ [theGrabber doSomethingWithParameter:someParameter];
+ NSInvocation *theInvocation = [theGrabber invocation];
+
+ A slightly more concise version (using the covenience category) follows:
+
+ DDInvocationGrabber *theGrabber = [DDInvocationGrabber invocationGrabber];
+ [[theGrabber prepareWithInvocationTarget:someObject]
+    doSomethingWithParameter:someParameter];
+ NSInvocation *theInvocation = [theGrabber invocation];
+
+ */
+@interface DDInvocationGrabber : NSProxy
+{
+    id _target;
+    NSInvocation * _invocation;
+    BOOL _forwardInvokesOnMainThread;
+    BOOL _waitUntilDone;
+}
+
+/**
+ * @method invocationGrabber
+ * @abstract Returns a newly allocated, inited, autoreleased
+ * DDInvocationGrabber object.
+ */
++ (id) invocationGrabber;
+
+- (id) target;
+- (void) setTarget:(id)inTarget;
+
+- (NSInvocation *) invocation;
+- (void) setInvocation:(NSInvocation *)inInvocation;
+
+- (BOOL) forwardInvokesOnMainThread;
+- (void) setForwardInvokesOnMainThread:(BOOL)forwardInvokesOnMainThread;
+
+- (BOOL) waitUntilDone;
+- (void) setWaitUntilDone:(BOOL)waitUntilDone;
+
+@end
+
+@interface DDInvocationGrabber (DDInvocationGrabber_Conveniences)
+
+/**
+ * @method prepareWithInvocationTarget:
+ * @abstract Sets the target object of the receiver and returns itself.
+ * The sender can then send a message to the receiver.
+ */
+- (id)prepareWithInvocationTarget:(id)inTarget;
+
+@end
diff --git a/ui/DDInvocationGrabber.m b/ui/DDInvocationGrabber.m
new file mode 100644
index 0000000000..297a0b41f5
--- /dev/null
+++ b/ui/DDInvocationGrabber.m
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2007-2008 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+/*
+ *  This class is based on CInvocationGrabber:
+ *
+ *  Copyright (c) 2007, Toxic Software
+ *  All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of the Toxic Software nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import "DDInvocationGrabber.h"
+
+
+@implementation DDInvocationGrabber
+
++ (id)invocationGrabber
+{
+    return([[[self alloc] init] autorelease]);
+}
+
+- (id)init
+{
+    _target = nil;
+    _invocation = nil;
+    _forwardInvokesOnMainThread = NO;
+    _waitUntilDone = NO;
+
+    return self;
+}
+
+- (void)dealloc
+{
+    [self setTarget:NULL];
+    [self setInvocation:NULL];
+    //
+    [super dealloc];
+}
+
+#pragma mark -
+
+- (id)target
+{
+    return _target;
+}
+
+- (void)setTarget:(id)inTarget
+{
+    if (_target != inTarget)
+	{
+        [_target autorelease];
+        _target = [inTarget retain];
+	}
+}
+
+- (NSInvocation *)invocation
+{
+    return _invocation;
+}
+
+- (void)setInvocation:(NSInvocation *)inInvocation
+{
+    if (_invocation != inInvocation)
+	{
+        [_invocation autorelease];
+        _invocation = [inInvocation retain];
+	}
+}
+
+- (BOOL)forwardInvokesOnMainThread;
+{
+    return _forwardInvokesOnMainThread;
+}
+
+- (void)setForwardInvokesOnMainThread:(BOOL)forwardInvokesOnMainThread;
+{
+    _forwardInvokesOnMainThread = forwardInvokesOnMainThread;
+}
+
+- (BOOL)waitUntilDone;
+{
+    return _waitUntilDone;
+}
+
+- (void)setWaitUntilDone:(BOOL)waitUntilDone;
+{
+    _waitUntilDone = waitUntilDone;
+}
+
+#pragma mark -
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
+{
+    return [[self target] methodSignatureForSelector:selector];
+}
+
+- (void)forwardInvocation:(NSInvocation *)ioInvocation
+{
+    [ioInvocation setTarget:[self target]];
+    [self setInvocation:ioInvocation];
+    if (_forwardInvokesOnMainThread)
+    {
+        if (!_waitUntilDone)
+            [_invocation retainArguments];
+        [_invocation performSelectorOnMainThread:@selector(invoke)
+                                      withObject:nil
+                                   waitUntilDone:_waitUntilDone];
+    }
+}
+
+@end
+
+#pragma mark -
+
+@implementation DDInvocationGrabber (DDnvocationGrabber_Conveniences)
+
+- (id)prepareWithInvocationTarget:(id)inTarget
+{
+    [self setTarget:inTarget];
+    return(self);
+}
+
+@end
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 00f6976c30..bd9983232f 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -11,7 +11,7 @@ common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
 common-obj-y += input.o input-keymap.o input-legacy.o
 common-obj-$(CONFIG_LINUX) += input-linux.o
 common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
-common-obj-$(CONFIG_COCOA) += cocoa.o
+common-obj-$(CONFIG_COCOA) += cocoa.o DDInvocationGrabber.o
 common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
 common-obj-$(call lnot,$(CONFIG_VNC)) += vnc-stubs.o
 
diff --git a/ui/cocoa.m b/ui/cocoa.m
index f69f7105f2..41aa7524d8 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -38,6 +38,8 @@
 #include <Carbon/Carbon.h>
 #include "qom/cpu.h"
 
+#import "DDInvocationGrabber.h"
+
 #ifndef MAC_OS_X_VERSION_10_5
 #define MAC_OS_X_VERSION_10_5 1050
 #endif
@@ -314,6 +316,8 @@ static void handleAnyDeviceErrors(Error * err)
 - (float) cdy;
 - (QEMUScreen) gscreen;
 - (void) raiseAllKeys;
+- (void) refresh;
+- (id) invokeOnMainThread;
 @end
 
 QemuCocoaView *cocoaView;
@@ -345,6 +349,14 @@ QemuCocoaView *cocoaView;
     [super dealloc];
 }
 
+- (id) invokeOnMainThread
+{
+    DDInvocationGrabber * grabber = [DDInvocationGrabber invocationGrabber];
+    [grabber setForwardInvokesOnMainThread:YES];
+    [grabber setWaitUntilDone:YES];
+    return [grabber prepareWithInvocationTarget:self];
+}
+
 - (BOOL) isOpaque
 {
     return YES;
@@ -509,6 +521,28 @@ QemuCocoaView *cocoaView;
     }
 }
 
+- (void) refresh
+{
+    if (qemu_input_is_absolute()) {
+        if (![self isAbsoluteEnabled]) {
+            if ([self isMouseGrabbed]) {
+                [self ungrabMouse];
+            }
+        }
+        [self setAbsoluteEnabled:YES];
+    }
+
+    NSDate *distantPast = [NSDate distantPast];
+    NSEvent *event;
+    do {
+        event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast
+                        inMode: NSDefaultRunLoopMode dequeue:YES];
+        if (event != nil) {
+            [self handleEvent:event];
+        }
+    } while(event != nil);
+}
+
 - (void) toggleFullScreen:(id)sender
 {
     COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
@@ -1566,7 +1600,7 @@ static void cocoa_update(DisplayChangeListener *dcl,
             w * [cocoaView cdx],
             h * [cocoaView cdy]);
     }
-    [cocoaView setNeedsDisplayInRect:rect];
+    [[cocoaView invokeOnMainThread] setNeedsDisplayInRect:rect];
 
     [pool release];
 }
@@ -1577,7 +1611,7 @@ static void cocoa_switch(DisplayChangeListener *dcl,
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
     COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
-    [cocoaView switchSurface:surface];
+    [[cocoaView invokeOnMainThread] switchSurface:surface];
     [pool release];
 }
 
@@ -1588,25 +1622,8 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
     COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
     graphic_hw_update(NULL);
 
-    if (qemu_input_is_absolute()) {
-        if (![cocoaView isAbsoluteEnabled]) {
-            if ([cocoaView isMouseGrabbed]) {
-                [cocoaView ungrabMouse];
-            }
-        }
-        [cocoaView setAbsoluteEnabled:YES];
-    }
+    [[cocoaView invokeOnMainThread] refresh];
 
-    NSDate *distantPast;
-    NSEvent *event;
-    distantPast = [NSDate distantPast];
-    do {
-        event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast
-                        inMode: NSDefaultRunLoopMode dequeue:YES];
-        if (event != nil) {
-            [cocoaView handleEvent:event];
-        }
-    } while(event != nil);
     [pool release];
 }
 
-- 
2.18.0


[-- Attachment #2: 0001-ui-cocoa-Defer-qemu-to-another-thread-leaving-main-t.patch --]
[-- Type: application/octet-stream, Size: 1216 bytes --]

From 8f86e30f3710d782d78dccdbe7a1564ae79220c7 Mon Sep 17 00:00:00 2001
From: Berkus Decker <berkus+github@metta.systems>
Date: Sun, 11 Nov 2018 21:58:17 +0200
Subject: [PATCH 1/2] ui/cocoa: Defer qemu to another thread, leaving main
 thread for the UI

This prevents blocking in applicationDidFinishLoading: which is
not recommended and now causes complete UI lock on macOS Mojave.

Signed-off-by: Berkus Decker <berkus+github@metta.systems>
---
 ui/cocoa.m | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/ui/cocoa.m b/ui/cocoa.m
index ecf12bfc2e..f69f7105f2 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -1089,9 +1089,13 @@ QemuCocoaView *cocoaView;
 {
     COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
 
-    int status;
-    status = qemu_main(argc, argv, *_NSGetEnviron());
-    exit(status);
+    dispatch_queue_t qemu_runner = dispatch_queue_create("qemu-runner", DISPATCH_QUEUE_SERIAL);
+
+    dispatch_async(qemu_runner, ^{
+        int status;
+        status = qemu_main(argc, argv, *_NSGetEnviron());
+        exit(status);
+    });
 }
 
 /* We abstract the method called by the Enter Fullscreen menu item
-- 
2.18.0


[-- Attachment #3: 0002-ui-cocoa-Fix-UI-crashes-on-macOS-Mojave.patch --]
[-- Type: application/octet-stream, Size: 15012 bytes --]

From 467b0f67d94616ef98d2ec1e8d16eeb5e9506b4e Mon Sep 17 00:00:00 2001
From: Berkus Decker <berkus+github@metta.systems>
Date: Sun, 11 Nov 2018 20:22:01 +0200
Subject: [PATCH 2/2] ui/cocoa: Fix UI crashes on macOS Mojave

Signed-off-by: Berkus Decker <berkus+github@metta.systems>
---
 ui/DDInvocationGrabber.h | 124 ++++++++++++++++++++++++++++
 ui/DDInvocationGrabber.m | 171 +++++++++++++++++++++++++++++++++++++++
 ui/Makefile.objs         |   2 +-
 ui/cocoa.m               |  57 ++++++++-----
 4 files changed, 333 insertions(+), 21 deletions(-)
 create mode 100644 ui/DDInvocationGrabber.h
 create mode 100644 ui/DDInvocationGrabber.m

diff --git a/ui/DDInvocationGrabber.h b/ui/DDInvocationGrabber.h
new file mode 100644
index 0000000000..7218421d74
--- /dev/null
+++ b/ui/DDInvocationGrabber.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2007-2008 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+/*
+ *  This class is based on CInvocationGrabber:
+ *
+ *  Copyright (c) 2007, Toxic Software
+ *  All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of the Toxic Software nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+/**
+ * @class DDInvocationGrabber
+ * @discussion DDInvocationGrabber is a helper object that makes it very easy
+ * to construct instances of NSInvocation for later use. The object is
+ * inspired by NSUndoManager's prepareWithInvocationTarget method. To use
+ * a DDInvocationGrabber object, you set its target to some object, then send
+ * it a message as if it were the target object (the DDInvocationGrabber object
+ * acts as a proxy), if the target message understands the message the
+ * DDInvocationGrabber object stores the message invocation.
+
+ DDInvocationGrabber *theGrabber = [DDInvocationGrabber invocationGrabber];
+ [theGrabber setTarget:someObject]
+ // Send messages to 'theGrabber' as if it were 'someObject'
+ [theGrabber doSomethingWithParameter:someParameter];
+ NSInvocation *theInvocation = [theGrabber invocation];
+
+ A slightly more concise version (using the covenience category) follows:
+
+ DDInvocationGrabber *theGrabber = [DDInvocationGrabber invocationGrabber];
+ [[theGrabber prepareWithInvocationTarget:someObject]
+    doSomethingWithParameter:someParameter];
+ NSInvocation *theInvocation = [theGrabber invocation];
+
+ */
+@interface DDInvocationGrabber : NSProxy
+{
+    id _target;
+    NSInvocation * _invocation;
+    BOOL _forwardInvokesOnMainThread;
+    BOOL _waitUntilDone;
+}
+
+/**
+ * @method invocationGrabber
+ * @abstract Returns a newly allocated, inited, autoreleased
+ * DDInvocationGrabber object.
+ */
++ (id) invocationGrabber;
+
+- (id) target;
+- (void) setTarget:(id)inTarget;
+
+- (NSInvocation *) invocation;
+- (void) setInvocation:(NSInvocation *)inInvocation;
+
+- (BOOL) forwardInvokesOnMainThread;
+- (void) setForwardInvokesOnMainThread:(BOOL)forwardInvokesOnMainThread;
+
+- (BOOL) waitUntilDone;
+- (void) setWaitUntilDone:(BOOL)waitUntilDone;
+
+@end
+
+@interface DDInvocationGrabber (DDInvocationGrabber_Conveniences)
+
+/**
+ * @method prepareWithInvocationTarget:
+ * @abstract Sets the target object of the receiver and returns itself.
+ * The sender can then send a message to the receiver.
+ */
+- (id)prepareWithInvocationTarget:(id)inTarget;
+
+@end
diff --git a/ui/DDInvocationGrabber.m b/ui/DDInvocationGrabber.m
new file mode 100644
index 0000000000..297a0b41f5
--- /dev/null
+++ b/ui/DDInvocationGrabber.m
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2007-2008 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+/*
+ *  This class is based on CInvocationGrabber:
+ *
+ *  Copyright (c) 2007, Toxic Software
+ *  All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of the Toxic Software nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import "DDInvocationGrabber.h"
+
+
+@implementation DDInvocationGrabber
+
++ (id)invocationGrabber
+{
+    return([[[self alloc] init] autorelease]);
+}
+
+- (id)init
+{
+    _target = nil;
+    _invocation = nil;
+    _forwardInvokesOnMainThread = NO;
+    _waitUntilDone = NO;
+
+    return self;
+}
+
+- (void)dealloc
+{
+    [self setTarget:NULL];
+    [self setInvocation:NULL];
+    //
+    [super dealloc];
+}
+
+#pragma mark -
+
+- (id)target
+{
+    return _target;
+}
+
+- (void)setTarget:(id)inTarget
+{
+    if (_target != inTarget)
+	{
+        [_target autorelease];
+        _target = [inTarget retain];
+	}
+}
+
+- (NSInvocation *)invocation
+{
+    return _invocation;
+}
+
+- (void)setInvocation:(NSInvocation *)inInvocation
+{
+    if (_invocation != inInvocation)
+	{
+        [_invocation autorelease];
+        _invocation = [inInvocation retain];
+	}
+}
+
+- (BOOL)forwardInvokesOnMainThread;
+{
+    return _forwardInvokesOnMainThread;
+}
+
+- (void)setForwardInvokesOnMainThread:(BOOL)forwardInvokesOnMainThread;
+{
+    _forwardInvokesOnMainThread = forwardInvokesOnMainThread;
+}
+
+- (BOOL)waitUntilDone;
+{
+    return _waitUntilDone;
+}
+
+- (void)setWaitUntilDone:(BOOL)waitUntilDone;
+{
+    _waitUntilDone = waitUntilDone;
+}
+
+#pragma mark -
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
+{
+    return [[self target] methodSignatureForSelector:selector];
+}
+
+- (void)forwardInvocation:(NSInvocation *)ioInvocation
+{
+    [ioInvocation setTarget:[self target]];
+    [self setInvocation:ioInvocation];
+    if (_forwardInvokesOnMainThread)
+    {
+        if (!_waitUntilDone)
+            [_invocation retainArguments];
+        [_invocation performSelectorOnMainThread:@selector(invoke)
+                                      withObject:nil
+                                   waitUntilDone:_waitUntilDone];
+    }
+}
+
+@end
+
+#pragma mark -
+
+@implementation DDInvocationGrabber (DDnvocationGrabber_Conveniences)
+
+- (id)prepareWithInvocationTarget:(id)inTarget
+{
+    [self setTarget:inTarget];
+    return(self);
+}
+
+@end
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 00f6976c30..bd9983232f 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -11,7 +11,7 @@ common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
 common-obj-y += input.o input-keymap.o input-legacy.o
 common-obj-$(CONFIG_LINUX) += input-linux.o
 common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
-common-obj-$(CONFIG_COCOA) += cocoa.o
+common-obj-$(CONFIG_COCOA) += cocoa.o DDInvocationGrabber.o
 common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
 common-obj-$(call lnot,$(CONFIG_VNC)) += vnc-stubs.o
 
diff --git a/ui/cocoa.m b/ui/cocoa.m
index f69f7105f2..41aa7524d8 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -38,6 +38,8 @@
 #include <Carbon/Carbon.h>
 #include "qom/cpu.h"
 
+#import "DDInvocationGrabber.h"
+
 #ifndef MAC_OS_X_VERSION_10_5
 #define MAC_OS_X_VERSION_10_5 1050
 #endif
@@ -314,6 +316,8 @@ static void handleAnyDeviceErrors(Error * err)
 - (float) cdy;
 - (QEMUScreen) gscreen;
 - (void) raiseAllKeys;
+- (void) refresh;
+- (id) invokeOnMainThread;
 @end
 
 QemuCocoaView *cocoaView;
@@ -345,6 +349,14 @@ QemuCocoaView *cocoaView;
     [super dealloc];
 }
 
+- (id) invokeOnMainThread
+{
+    DDInvocationGrabber * grabber = [DDInvocationGrabber invocationGrabber];
+    [grabber setForwardInvokesOnMainThread:YES];
+    [grabber setWaitUntilDone:YES];
+    return [grabber prepareWithInvocationTarget:self];
+}
+
 - (BOOL) isOpaque
 {
     return YES;
@@ -509,6 +521,28 @@ QemuCocoaView *cocoaView;
     }
 }
 
+- (void) refresh
+{
+    if (qemu_input_is_absolute()) {
+        if (![self isAbsoluteEnabled]) {
+            if ([self isMouseGrabbed]) {
+                [self ungrabMouse];
+            }
+        }
+        [self setAbsoluteEnabled:YES];
+    }
+
+    NSDate *distantPast = [NSDate distantPast];
+    NSEvent *event;
+    do {
+        event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast
+                        inMode: NSDefaultRunLoopMode dequeue:YES];
+        if (event != nil) {
+            [self handleEvent:event];
+        }
+    } while(event != nil);
+}
+
 - (void) toggleFullScreen:(id)sender
 {
     COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
@@ -1566,7 +1600,7 @@ static void cocoa_update(DisplayChangeListener *dcl,
             w * [cocoaView cdx],
             h * [cocoaView cdy]);
     }
-    [cocoaView setNeedsDisplayInRect:rect];
+    [[cocoaView invokeOnMainThread] setNeedsDisplayInRect:rect];
 
     [pool release];
 }
@@ -1577,7 +1611,7 @@ static void cocoa_switch(DisplayChangeListener *dcl,
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
     COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
-    [cocoaView switchSurface:surface];
+    [[cocoaView invokeOnMainThread] switchSurface:surface];
     [pool release];
 }
 
@@ -1588,25 +1622,8 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
     COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
     graphic_hw_update(NULL);
 
-    if (qemu_input_is_absolute()) {
-        if (![cocoaView isAbsoluteEnabled]) {
-            if ([cocoaView isMouseGrabbed]) {
-                [cocoaView ungrabMouse];
-            }
-        }
-        [cocoaView setAbsoluteEnabled:YES];
-    }
+    [[cocoaView invokeOnMainThread] refresh];
 
-    NSDate *distantPast;
-    NSEvent *event;
-    distantPast = [NSDate distantPast];
-    do {
-        event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast
-                        inMode: NSDefaultRunLoopMode dequeue:YES];
-        if (event != nil) {
-            [cocoaView handleEvent:event];
-        }
-    } while(event != nil);
     [pool release];
 }
 
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-11 21:24 [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave Berkus Decker
@ 2018-11-20 12:59 ` Peter Maydell
  2018-11-21 15:12   ` Peter Maydell
  0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2018-11-20 12:59 UTC (permalink / raw)
  To: Berkus Decker; +Cc: QEMU Developers

On 11 November 2018 at 21:24, Berkus Decker <berkus@gmail.com> wrote:
> It seems that Cocoa checks are stricter on Mojave and some callbacks that worked from non-GUI thread on High Sierra are no longer working.
>
> The fixes included here are:
>
> * Deferring qemu_main() to another thread so that the actual main thread is reserved for the Cocoa UI; it also removes blocking from applicationDidFinishLoading: delegate. I beleive this alone caused complete UI blockage on Mojave.
> * Deferring UI-related updates in callbacks to the UI thread using invokeOnMainThread helper function. This function uses DDInvocationGrabber object courtesy of Dave Dribin, licensed under MIT license.
> Here’s relevant blog post for his code: https://www.dribin.org/dave/blog/archives/2008/05/22/invoke_on_main_thread/
>
> NSInvocation is used here instead of plain performSelectorOnMainThread:withObject:waitUntilDone: because we want to be able to pass non-id types to the handlers.
>
> These changes are ought to work on OSX 10.6, although I don’t have a machine handy to test it.
>
> Fixes https://bugs.launchpad.net/qemu/+bug/1802684

Hi; thanks for these patches. I haven't tried running them yet
(I should be able to get to that tomorrow) I have done a compile
test on High Sierra. I have some code review comments below:

> From 8f86e30f3710d782d78dccdbe7a1564ae79220c7 Mon Sep 17 00:00:00 2001
> From: Berkus Decker <berkus+github@metta.systems>
> Date: Sun, 11 Nov 2018 21:58:17 +0200
> Subject: [PATCH 1/2] ui/cocoa: Defer qemu to another thread, leaving main
>  thread for the UI
>
> This prevents blocking in applicationDidFinishLoading: which is
> not recommended and now causes complete UI lock on macOS Mojave.
>
> Signed-off-by: Berkus Decker <berkus+github@metta.systems>

If you could send your patchset in git format-patch style as
multiple emails (a cover letter, and each patch in the series
a followup to the cover letter) that would be helpful, as
our patch-handling tooling assumes that setup. But if not
I can manually sort things out at my end.

> ---
>  ui/cocoa.m | 10 +++++++---
>  1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/ui/cocoa.m b/ui/cocoa.m
> index ecf12bfc2e..f69f7105f2 100644
> --- a/ui/cocoa.m
> +++ b/ui/cocoa.m
> @@ -1089,9 +1089,13 @@ QemuCocoaView *cocoaView;
>  {
>      COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
>
> -    int status;
> -    status = qemu_main(argc, argv, *_NSGetEnviron());
> -    exit(status);
> +    dispatch_queue_t qemu_runner = dispatch_queue_create("qemu-runner", DISPATCH_QUEUE_SERIAL);

I suspect this won't compile on 10.6:
https://developer.apple.com/documentation/dispatch/1453030-dispatch_queue_create
says that DISPATCH_QUEUE_SERIAL is 10.7 or later and earlier
versions need to pass NULL.

> +
> +    dispatch_async(qemu_runner, ^{
> +        int status;
> +        status = qemu_main(argc, argv, *_NSGetEnviron());
> +        exit(status);
> +    });
>  }
>
>  /* We abstract the method called by the Enter Fullscreen menu item
> --
> 2.18.0
>
>
> From 467b0f67d94616ef98d2ec1e8d16eeb5e9506b4e Mon Sep 17 00:00:00 2001
> From: Berkus Decker <berkus+github@metta.systems>
> Date: Sun, 11 Nov 2018 20:22:01 +0200
> Subject: [PATCH 2/2] ui/cocoa: Fix UI crashes on macOS Mojave
>
> Signed-off-by: Berkus Decker <berkus+github@metta.systems>
> ---
>  ui/DDInvocationGrabber.h | 124 ++++++++++++++++++++++++++++
>  ui/DDInvocationGrabber.m | 171 +++++++++++++++++++++++++++++++++++++++
>  ui/Makefile.objs         |   2 +-
>  ui/cocoa.m               |  57 ++++++++-----
>  4 files changed, 333 insertions(+), 21 deletions(-)
>  create mode 100644 ui/DDInvocationGrabber.h
>  create mode 100644 ui/DDInvocationGrabber.m

I think it would be good to separate out this commit into two:
 * ui/cocoa: Add Dave Drivbin's DDInvocationGrabber
   (which would add the two new files and change ui/Makefile.objs
   to compile the .m file)
 * ui/cocoa: Fix UI crashes on macOS Mojave
   (which just has the ui/cocoa.m changes)

Then we have one patch whose review is just "check the license"
and one patch which is the parts that require review of the code.

(If we were willing to drop support for OSX before 10.10
we could use dispatch_sync(dispatch_get_main_queue(), ...).
Thanks for finding this bit of code to keep things working
for 10.6. It does feel a little heavyweight but on the other
hand it's existing working code.)

> diff --git a/ui/cocoa.m b/ui/cocoa.m
> index f69f7105f2..41aa7524d8 100644
> --- a/ui/cocoa.m
> +++ b/ui/cocoa.m
> @@ -38,6 +38,8 @@
>  #include <Carbon/Carbon.h>
>  #include "qom/cpu.h"
>
> +#import "DDInvocationGrabber.h"
> +
>  #ifndef MAC_OS_X_VERSION_10_5
>  #define MAC_OS_X_VERSION_10_5 1050
>  #endif
> @@ -314,6 +316,8 @@ static void handleAnyDeviceErrors(Error * err)
>  - (float) cdy;
>  - (QEMUScreen) gscreen;
>  - (void) raiseAllKeys;
> +- (void) refresh;
> +- (id) invokeOnMainThread;
>  @end
>
>  QemuCocoaView *cocoaView;
> @@ -345,6 +349,14 @@ QemuCocoaView *cocoaView;
>      [super dealloc];
>  }
>
> +- (id) invokeOnMainThread
> +{
> +    DDInvocationGrabber * grabber = [DDInvocationGrabber invocationGrabber];
> +    [grabber setForwardInvokesOnMainThread:YES];
> +    [grabber setWaitUntilDone:YES];
> +    return [grabber prepareWithInvocationTarget:self];
> +}
> +
>  - (BOOL) isOpaque
>  {
>      return YES;
> @@ -509,6 +521,28 @@ QemuCocoaView *cocoaView;
>      }
>  }
>
> +- (void) refresh
> +{
> +    if (qemu_input_is_absolute()) {
> +        if (![self isAbsoluteEnabled]) {
> +            if ([self isMouseGrabbed]) {
> +                [self ungrabMouse];
> +            }
> +        }
> +        [self setAbsoluteEnabled:YES];
> +    }
> +
> +    NSDate *distantPast = [NSDate distantPast];
> +    NSEvent *event;
> +    do {
> +        event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast
> +                        inMode: NSDefaultRunLoopMode dequeue:YES];
> +        if (event != nil) {
> +            [self handleEvent:event];
> +        }
> +    } while(event != nil);
> +}
> +
>  - (void) toggleFullScreen:(id)sender
>  {
>      COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
> @@ -1566,7 +1600,7 @@ static void cocoa_update(DisplayChangeListener *dcl,
>              w * [cocoaView cdx],
>              h * [cocoaView cdy]);
>      }
> -    [cocoaView setNeedsDisplayInRect:rect];
> +    [[cocoaView invokeOnMainThread] setNeedsDisplayInRect:rect];

Isn't there a race condition here? You've left the code
which calculates the rectangle size in the cocoa_update()
function, but it looks at properties like [cocoaView cdx]
which can be changed by user interactions like window resizing.
I think it would be better to push everything into the main
thread, so that "update screen" is atomic with respect to other
UI operations that might affect the screen coordinates.

We had this race before, of course, since the old cocoa_update()
ran on a different thread to the main event loop; but we have
the opportunity to fix it since we're pushing the code into
the main thread now.

>      [pool release];
>  }
> @@ -1577,7 +1611,7 @@ static void cocoa_switch(DisplayChangeListener *dcl,
>      NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
>
>      COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
> -    [cocoaView switchSurface:surface];
> +    [[cocoaView invokeOnMainThread] switchSurface:surface];
>      [pool release];
>  }
>
> @@ -1588,25 +1622,8 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
>      COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
>      graphic_hw_update(NULL);
>
> -    if (qemu_input_is_absolute()) {
> -        if (![cocoaView isAbsoluteEnabled]) {
> -            if ([cocoaView isMouseGrabbed]) {
> -                [cocoaView ungrabMouse];
> -            }
> -        }
> -        [cocoaView setAbsoluteEnabled:YES];
> -    }
> +    [[cocoaView invokeOnMainThread] refresh];
>
> -    NSDate *distantPast;
> -    NSEvent *event;
> -    distantPast = [NSDate distantPast];
> -    do {
> -        event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast
> -                        inMode: NSDefaultRunLoopMode dequeue:YES];
> -        if (event != nil) {
> -            [cocoaView handleEvent:event];
> -        }
> -    } while(event != nil);
>      [pool release];
>  }
>
> --
> 2.18.0

thanks
-- PMM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-20 12:59 ` Peter Maydell
@ 2018-11-21 15:12   ` Peter Maydell
  2018-11-22  7:32     ` Gerd Hoffmann
  0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2018-11-21 15:12 UTC (permalink / raw)
  To: Berkus Decker; +Cc: QEMU Developers, Gerd Hoffmann

(Hi Gerd -- I have a question at the bottom of this email
about the thread/locking semantics of the UI midlayer...)

On 20 November 2018 at 12:59, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 11 November 2018 at 21:24, Berkus Decker <berkus@gmail.com> wrote:
>> It seems that Cocoa checks are stricter on Mojave and some callbacks that worked from non-GUI thread on High Sierra are no longer working.
>>
>> The fixes included here are:
>>
>> * Deferring qemu_main() to another thread so that the actual main thread is reserved for the Cocoa UI; it also removes blocking from applicationDidFinishLoading: delegate. I beleive this alone caused complete UI blockage on Mojave.
>> * Deferring UI-related updates in callbacks to the UI thread using invokeOnMainThread helper function. This function uses DDInvocationGrabber object courtesy of Dave Dribin, licensed under MIT license.
>> Here’s relevant blog post for his code: https://www.dribin.org/dave/blog/archives/2008/05/22/invoke_on_main_thread/
>>
>> NSInvocation is used here instead of plain performSelectorOnMainThread:withObject:waitUntilDone: because we want to be able to pass non-id types to the handlers.
>>
>> These changes are ought to work on OSX 10.6, although I don’t have a machine handy to test it.
>>
>> Fixes https://bugs.launchpad.net/qemu/+bug/1802684
>
> Hi; thanks for these patches. I haven't tried running them yet
> (I should be able to get to that tomorrow)

On High Sierra this does run for me, but seems to
introduce a bug:

 * Run qemu-system-x86_64 with no arguments
 * Let the BIOS get to the "No bootable devices" message
 * Press a key in the QEMU window

QEMU hits an assert:
ERROR:/Users/pm215/src/qemu/accel/tcg/tcg-all.c:42:tcg_handle_interrupt:
assertion failed: (qemu_mutex_iothread_locked())

with this backtrace:

* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff7b123b66 libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff7b2ee080 libsystem_pthread.dylib`pthread_kill + 333
    frame #2: 0x00007fff7b07f1ae libsystem_c.dylib`abort + 127
    frame #3: 0x00000001014f7f19 libglib-2.0.0.dylib`g_assertion_message + 407
    frame #4: 0x00000001014f7f77
libglib-2.0.0.dylib`g_assertion_message_expr + 94
    frame #5: 0x00000001000a3509
qemu-system-x86_64`tcg_handle_interrupt(cpu=0x0000000103883200,
mask=2) at tcg-all.c:42
    frame #6: 0x00000001000e8222
qemu-system-x86_64`cpu_interrupt(cpu=0x0000000103883200, mask=2) at
cpu.h:855
    frame #7: 0x00000001000e70ca
qemu-system-x86_64`apic_local_deliver(s=0x0000000101e40010, vector=3)
at apic.c:156
    frame #8: 0x00000001000e6f71
qemu-system-x86_64`apic_deliver_pic_intr(dev=0x0000000101e40010,
level=1) at apic.c:173
    frame #9: 0x000000010010ff9e
qemu-system-x86_64`pic_irq_request(opaque=0x0000000000000000, irq=0,
level=1) at pc.c:195
    frame #10: 0x0000000100297f3b
qemu-system-x86_64`qemu_set_irq(irq=0x0000000101cdac30, level=1) at
irq.c:45
    frame #11: 0x000000010030073a
qemu-system-x86_64`qemu_irq_raise(irq=0x0000000101cdac30) at irq.h:16
    frame #12: 0x00000001003006ad
qemu-system-x86_64`pic_update_irq(s=0x0000000101cdada0) at i8259.c:111
    frame #13: 0x0000000100300d1c
qemu-system-x86_64`pic_set_irq(opaque=0x0000000101cdada0, irq=1,
level=1) at i8259.c:153
    frame #14: 0x0000000100297f3b
qemu-system-x86_64`qemu_set_irq(irq=0x0000000101cdbf50, level=1) at
irq.c:45
    frame #15: 0x000000010010cad8
qemu-system-x86_64`gsi_handler(opaque=0x0000000101cc7760, n=1,
level=1) at pc.c:118
    frame #16: 0x0000000100297f3b
qemu-system-x86_64`qemu_set_irq(irq=0x0000000101cc7410, level=1) at
irq.c:45
    frame #17: 0x00000001002fa162
qemu-system-x86_64`kbd_update_irq(s=0x0000000101e5cef8) at pckbd.c:181
    frame #18: 0x00000001002f967f
qemu-system-x86_64`kbd_update_kbd_irq(opaque=0x0000000101e5cef8,
level=1) at pckbd.c:193
    frame #19: 0x00000001002fa603
qemu-system-x86_64`ps2_queue(s=0x0000000101e5e490, b=22) at ps2.c:213
    frame #20: 0x00000001002fadd4
qemu-system-x86_64`ps2_put_keycode(opaque=0x0000000101e5e490,
keycode=60) at ps2.c:267
    frame #21: 0x00000001002fd10f
qemu-system-x86_64`ps2_keyboard_event(dev=0x0000000101e5e490,
src=0x0000000000000000, evt=0x0000000101af02c0) at ps2.c:470
    frame #22: 0x0000000100491e2c
qemu-system-x86_64`qemu_input_event_send_impl(src=0x0000000000000000,
evt=0x0000000101af02c0) at input.c:346
    frame #23: 0x000000010046c8d5
qemu-system-x86_64`replay_input_event(src=0x0000000000000000,
evt=0x0000000101af02c0) at replay-input.c:128
    frame #24: 0x0000000100491d5b
qemu-system-x86_64`qemu_input_event_send(src=0x0000000000000000,
evt=0x0000000101af02c0) at input.c:375
    frame #25: 0x0000000100492297
qemu-system-x86_64`qemu_input_event_send_key(src=0x0000000000000000,
key=0x00000001159037f0, down=true) at input.c:419
    frame #26: 0x0000000100491c71
qemu-system-x86_64`qemu_input_event_send_key_qcode(src=0x0000000000000000,
q=Q_KEY_CODE_U, down=true) at input.c:441
    frame #27: 0x0000000100496a0b qemu-system-x86_64`-[QemuCocoaView
handleEvent:](self=0x0000000101c2eef0, _cmd="handleEvent:",
event=0x0000000101cebdf0) at cocoa.m:767
    frame #28: 0x0000000100495b4e qemu-system-x86_64`-[QemuCocoaView
refresh](self=0x0000000101c2eef0, _cmd="refresh") at cocoa.m:541
    frame #29: 0x00007fff5301139c CoreFoundation`__invoking___ + 140
    frame #30: 0x00007fff53011270 CoreFoundation`-[NSInvocation invoke] + 320
    frame #31: 0x00007fff551730b5 Foundation`__NSThreadPerformPerform + 334
    frame #32: 0x00007fff53031be1
CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
+ 17
    frame #33: 0x00007fff530e94bc CoreFoundation`__CFRunLoopDoSource0 + 108
    frame #34: 0x00007fff53014b90 CoreFoundation`__CFRunLoopDoSources0 + 208
    frame #35: 0x00007fff5301400d CoreFoundation`__CFRunLoopRun + 1293
    frame #36: 0x00007fff53013867 CoreFoundation`CFRunLoopRunSpecific + 487
    frame #37: 0x00007fff522f3d96 HIToolbox`RunCurrentEventLoopInMode + 286
    frame #38: 0x00007fff522f3b06 HIToolbox`ReceiveNextEventCommon + 613
    frame #39: 0x00007fff522f3884
HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 64
    frame #40: 0x00007fff505a3a73 AppKit`_DPSNextEvent + 2085
    frame #41: 0x00007fff50d39e34 AppKit`-[NSApplication(NSEvent)
_nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 3044
    frame #42: 0x00007fff50598885 AppKit`-[NSApplication run] + 764
    frame #43: 0x000000010049aac1 qemu-system-x86_64`main(argc=1,
argv=0x00007ffeefbffa40) at cocoa.m:1575
    frame #44: 0x00007fff7afd3015 libdyld.dylib`start + 1

Something somewhere in this call chain should have taken
the iothread lock, I assume, but I'm not sure where.

Gerd -- what are the rules of the UI subsystem regarding
multiple threads, and what threads are allowed to call
UI functions like qemu_input_event_send_key_qcode(),
from which threads, and whether they need to eg hold
the iothread lock when they do so? There's no
documentation on these functions in include/ui/input.h.

(To fix a problem on OSX Mojave this patchset has moved
which thread the UI executes on, so it is now always
the main thread and not the thread which calls
the QemuDisplay 'init' callback. That seems to break
an implicit assumption in the UI layer.)

thanks
-- PMM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-21 15:12   ` Peter Maydell
@ 2018-11-22  7:32     ` Gerd Hoffmann
  2018-11-25 20:43       ` Peter Maydell
  0 siblings, 1 reply; 13+ messages in thread
From: Gerd Hoffmann @ 2018-11-22  7:32 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Berkus Decker, QEMU Developers

  Hi,

> Something somewhere in this call chain should have taken
> the iothread lock, I assume, but I'm not sure where.
> 
> Gerd -- what are the rules of the UI subsystem regarding
> multiple threads, and what threads are allowed to call
> UI functions like qemu_input_event_send_key_qcode(),
> from which threads, and whether they need to eg hold
> the iothread lock when they do so? There's no
> documentation on these functions in include/ui/input.h.

UI event handling code typically runs in iothread context.  So, yes,
when calling qemu_input_* the UI code holds the iothread lock.

> (To fix a problem on OSX Mojave this patchset has moved
> which thread the UI executes on, so it is now always
> the main thread and not the thread which calls
> the QemuDisplay 'init' callback. That seems to break
> an implicit assumption in the UI layer.)

Hmm, I guess the options are (a) grab the iothread lock before calling
input layer functions, or (b) queue the event and schedule a bottom half
which processes the queue (which will then be called from iothread
context, with the lock already taken).

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-22  7:32     ` Gerd Hoffmann
@ 2018-11-25 20:43       ` Peter Maydell
  2018-11-26  8:28         ` Gerd Hoffmann
  0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2018-11-25 20:43 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Berkus Decker, QEMU Developers

On Thu, 22 Nov 2018 at 07:32, Gerd Hoffmann <kraxel@redhat.com> wrote:
>
>   Hi,
>
> > Something somewhere in this call chain should have taken
> > the iothread lock, I assume, but I'm not sure where.
> >
> > Gerd -- what are the rules of the UI subsystem regarding
> > multiple threads, and what threads are allowed to call
> > UI functions like qemu_input_event_send_key_qcode(),
> > from which threads, and whether they need to eg hold
> > the iothread lock when they do so? There's no
> > documentation on these functions in include/ui/input.h.
>
> UI event handling code typically runs in iothread context.  So, yes,
> when calling qemu_input_* the UI code holds the iothread lock.
>
> > (To fix a problem on OSX Mojave this patchset has moved
> > which thread the UI executes on, so it is now always
> > the main thread and not the thread which calls
> > the QemuDisplay 'init' callback. That seems to break
> > an implicit assumption in the UI layer.)
>
> Hmm, I guess the options are (a) grab the iothread lock before calling
> input layer functions, or (b) queue the event and schedule a bottom half
> which processes the queue (which will then be called from iothread
> context, with the lock already taken).

I was thinking about this today, and I realized that if
we just make the OSX UI thread code grab the iothread lock,
then there is a potential deadlock with the changes (also
in this patchset) which defer the DisplayChangeListener
update/switch/refresh operations to the UI thread. The
current patchset has those be synchronous deferrals, so
you could get a deadlock if the UI thread is waiting
for the iothread lock, but the code in the UI midlayer
is holding the iothread lock and waiting for a DCL
operation to be run on the main thread.

What are the required semantics for update/switch/refresh?
These don't seem to be documented. Can we validly make
those be asychronous, or does the midlayer require that
the operation has completed and been reflected onscreen
before the update/switch/refresh callback returns ?

If those have to be synchronous, then we'll need to do
something more complicated (eg the queuing of events
that you suggest), which I'd prefer it if we can avoid,
because that implies memory allocation or a fixed
length queue (plus some fairly significant restructuring
of the cocoa frontend).

thanks
-- PMM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-25 20:43       ` Peter Maydell
@ 2018-11-26  8:28         ` Gerd Hoffmann
  0 siblings, 0 replies; 13+ messages in thread
From: Gerd Hoffmann @ 2018-11-26  8:28 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Berkus Decker, QEMU Developers

  Hi,

> What are the required semantics for update/switch/refresh?
> These don't seem to be documented. Can we validly make
> those be asychronous, or does the midlayer require that
> the operation has completed and been reflected onscreen
> before the update/switch/refresh callback returns ?


refresh
-------

refresh() is called at regular intervals, for guest display refresh.  By
default it is called every 30 ms (GUI_REFRESH_INTERVAL_DEFAULT), UIs can
ask for a different interval using update_displaychangelistener() though.

Typically the refresh() callback will first call
graphic_hw_update(vd->dcl.con), which in turn calls into the display
device emulation for display update checks.  vga emulation will check
dirty tracking here to figure whenever the guest has updated the
display.

Next is actually updating the UI (but see below).


switch
------

Notifies the UI that the DisplaySurface has changed.  Will happen when
the guest switches the video mode.  Can also happen on pageflips.

Can happen during the graphic_hw_update() call, but can also happen
independant from that.

Qemu will free the old DisplaySurface after the switch callback returns,
so you must make sure you don't keep a reference.  These days the
DisplaySurface is just a thin wrapper around a pixman image though, and
pixman images are reference counted.  So if you want handle display
updates outside the iothread the best way to do that is to grab the
pixman image (DisplaySurface->image), get a reference to make sure it is
not released under your feet (pixman_image_ref()) and run with that in
your UI thread.


update
------

Notifies the UI that the guest has updated a specific display rectangle
and the UI should redraw it.

Can happen during the graphic_hw_update() call, but can also happen
independant from that.

You can do the UI update right here, or you can just record the
rectangles, then do a batched update in the refresh() callback.  Or
you can pass on the rectangle to your UI thread for processing.

HTH,
  Gerd

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-21 10:53 ` Programmingkid
@ 2018-11-21 11:05   ` Daniel P. Berrangé
  0 siblings, 0 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2018-11-21 11:05 UTC (permalink / raw)
  To: Programmingkid; +Cc: QEMU Developers, Peter Maydell

On Wed, Nov 21, 2018 at 05:53:50AM -0500, Programmingkid wrote:
> 
> > On 13 November 2018 at 12:12, Programmingkid <programmingkidx@gmail.com> wrote:
> >>> On Nov 11, 2018, at 4:35 PM, Berkus Decker wrote:
> >>> These changes are ought to work on OSX 10.6, although I don?t have a machine handy to test it.
> >> 
> >> I have Mac OS 10.6 available for testing.
> > 
> > This is perhaps a good point to ask the question: is retaining
> > support for 10.6 still worth the effort ? It was released 9
> > years ago and has been unsupported by Apple for nearly 5 years.
> > 
> > qemu-doc.texi's "Supported build platforms" section says:
> > # The project supports building with the two most recent versions
> > # of macOS, with the current homebrew package set available.
> > 
> > which would be High Sierra (10.13) and Mojave (10.14) only. If you
> > widened that to "all versions still supported by Apple" it would add
> > Sierra (10.13) to the list.
> > 
> > Given how few upstream developers we have who care about OSX,
> > I think it is not very useful to spend that effort on trying
> > to retain support for ancient versions of it.
> > 
> > thanks
> > -- PMM
> 
> Mac OS 10.6 is a very good operating system. I much rather we simplify
> the patch rather than drop support for this excellent operation system
> just because it is a few years old. I suspect rewriting the the patch
> to use performSelectorOnMainThread:withObject:waitUntilDone: would be
> a simpler and better patch.

Whether 10.6 is a good OS or not is not really relevant. There are a great
many good operating systems that are no longer supported by either QEMU
or their vendors. It isn't a sensible use of limited project resources
to support an ever growing set of old OS versions. 

This is why we formalized our intentions wrt support for old operating
systems to limit it to a reasonable number of versions that are currently
supported by their respective vendors. We achieve a balance of keeping
enough platforms supported to suit 90+% of our expected users, while
keeping the set of platforms small enough that we can reasonably expect
to test them well & be able to utilize new features in a timely manner.

The goal of the support statement is further to stop endless debates
about whether we can use new features that aren't available in certain
old OS releases. The answer is now that we can use whatever features
provide the best solution for the OS versions that we explicitly
target. If that causes us to drop an old OS as a side-effect that is
entirely fine.

> I have tried the patch out already on Mac OS 10.12 and it did not work.
> The firmware never runs and the Machine menu does not populate. I do
> not believe this patch is worth dropping the support of many versions
> of Mac OS X.

Of course I'm not suggesting merging the patch if it doens't even work
for modern macOS versions. We should clearly fix the patch to work as
needed on our supported versions of macOS.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
       [not found] <mailman.23054.1542721275.1282.qemu-devel@nongnu.org>
@ 2018-11-21 10:53 ` Programmingkid
  2018-11-21 11:05   ` Daniel P. Berrangé
  0 siblings, 1 reply; 13+ messages in thread
From: Programmingkid @ 2018-11-21 10:53 UTC (permalink / raw)
  To: QEMU Developers, Peter Maydell


> On 13 November 2018 at 12:12, Programmingkid <programmingkidx@gmail.com> wrote:
>>> On Nov 11, 2018, at 4:35 PM, Berkus Decker wrote:
>>> These changes are ought to work on OSX 10.6, although I don?t have a machine handy to test it.
>> 
>> I have Mac OS 10.6 available for testing.
> 
> This is perhaps a good point to ask the question: is retaining
> support for 10.6 still worth the effort ? It was released 9
> years ago and has been unsupported by Apple for nearly 5 years.
> 
> qemu-doc.texi's "Supported build platforms" section says:
> # The project supports building with the two most recent versions
> # of macOS, with the current homebrew package set available.
> 
> which would be High Sierra (10.13) and Mojave (10.14) only. If you
> widened that to "all versions still supported by Apple" it would add
> Sierra (10.13) to the list.
> 
> Given how few upstream developers we have who care about OSX,
> I think it is not very useful to spend that effort on trying
> to retain support for ancient versions of it.
> 
> thanks
> -- PMM

Mac OS 10.6 is a very good operating system. I much rather we simplify the patch rather than drop support for this excellent operation system just because it is a few years old. I suspect rewriting the the patch to use performSelectorOnMainThread:withObject:waitUntilDone: would be a simpler and better patch.

I have tried the patch out already on Mac OS 10.12 and it did not work. The firmware never runs and the Machine menu does not populate. I do not believe this patch is worth dropping the support of many versions of Mac OS X.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-20 13:09   ` Peter Maydell
@ 2018-11-20 13:13     ` Daniel P. Berrangé
  0 siblings, 0 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2018-11-20 13:13 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Programmingkid, Berkus Decker, QEMU Developers

On Tue, Nov 20, 2018 at 01:09:54PM +0000, Peter Maydell wrote:
> On 13 November 2018 at 12:12, Programmingkid <programmingkidx@gmail.com> wrote:
> >> On Nov 11, 2018, at 4:35 PM, Berkus Decker wrote:
> >> These changes are ought to work on OSX 10.6, although I don?t have a machine handy to test it.
> >
> > I have Mac OS 10.6 available for testing.
> 
> This is perhaps a good point to ask the question: is retaining
> support for 10.6 still worth the effort ? It was released 9
> years ago and has been unsupported by Apple for nearly 5 years.
> 
> qemu-doc.texi's "Supported build platforms" section says:
> # The project supports building with the two most recent versions
> # of macOS, with the current homebrew package set available.
> 
> which would be High Sierra (10.13) and Mojave (10.14) only. If you
> widened that to "all versions still supported by Apple" it would add
> Sierra (10.13) to the list.
> 
> Given how few upstream developers we have who care about OSX,
> I think it is not very useful to spend that effort on trying
> to retain support for ancient versions of it.

Yes, given how little resource there is for OSX, and lack of any serious
automated testing beyond "does it compile", we should focus on host
versions which have biggest benefit to users, per our build platforms
guideline.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-13 12:12 ` Programmingkid
@ 2018-11-20 13:09   ` Peter Maydell
  2018-11-20 13:13     ` Daniel P. Berrangé
  0 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2018-11-20 13:09 UTC (permalink / raw)
  To: Programmingkid; +Cc: QEMU Developers, Berkus Decker

On 13 November 2018 at 12:12, Programmingkid <programmingkidx@gmail.com> wrote:
>> On Nov 11, 2018, at 4:35 PM, Berkus Decker wrote:
>> These changes are ought to work on OSX 10.6, although I don?t have a machine handy to test it.
>
> I have Mac OS 10.6 available for testing.

This is perhaps a good point to ask the question: is retaining
support for 10.6 still worth the effort ? It was released 9
years ago and has been unsupported by Apple for nearly 5 years.

qemu-doc.texi's "Supported build platforms" section says:
# The project supports building with the two most recent versions
# of macOS, with the current homebrew package set available.

which would be High Sierra (10.13) and Mojave (10.14) only. If you
widened that to "all versions still supported by Apple" it would add
Sierra (10.13) to the list.

Given how few upstream developers we have who care about OSX,
I think it is not very useful to spend that effort on trying
to retain support for ancient versions of it.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
  2018-11-14  0:53 ` Programmingkid
@ 2018-11-14 10:15   ` Berkus Decker
  0 siblings, 0 replies; 13+ messages in thread
From: Berkus Decker @ 2018-11-14 10:15 UTC (permalink / raw)
  To: Programmingkid; +Cc: QEMU Developers, Peter Maydell

I will double check on High Sierra and El Capitan.

So far I only tested qemu-system-aarch64 and it worked on Mojave, but did not test elsewhere (my aim was to get my osdev going, only).

I have a range of older osx machines (not as old as 10.6 though), so will put it through some rounds. Will post here with results.

Thanks for taking your time!

> On 14 Nov 2018, at 02:53, Programmingkid <programmingkidx@gmail.com> wrote:
> 
> 
>> On Nov 11, 2018, at 4:35 PM, qemu-devel-request@nongnu.org wrote:
>> 
>> It seems that Cocoa checks are stricter on Mojave and some callbacks that worked from non-GUI thread on High Sierra are no longer working.
>> 
>> The fixes included here are:
>> 
>> * Deferring qemu_main() to another thread so that the actual main thread is reserved for the Cocoa UI; it also removes blocking from applicationDidFinishLoading: delegate. I beleive this alone caused complete UI blockage on Mojave.
>> * Deferring UI-related updates in callbacks to the UI thread using invokeOnMainThread helper function. This function uses DDInvocationGrabber object courtesy of Dave Dribin, licensed under MIT license.
>> Here?s relevant blog post for his code: https://www.dribin.org/dave/blog/archives/2008/05/22/invoke_on_main_thread/
>> 
>> NSInvocation is used here instead of plain performSelectorOnMainThread:withObject:waitUntilDone: because we want to be able to pass non-id types to the handlers.
> 
> Also I realized that the only way we could call cocoa_refresh() from performSelectorOnMainThread:withObject:waitUntilDone: would be to add it to a class. 
> 
>> These changes are ought to work on OSX 10.6, although I don?t have a machine handy to test it.
>> 
>> Fixes https://bugs.launchpad.net/qemu/+bug/1802684
>> 
>> From 8f86e30f3710d782d78dccdbe7a1564ae79220c7 Mon Sep 17 00:00:00 2001
>> From: Berkus Decker <berkus+github@metta.systems>
>> Date: Sun, 11 Nov 2018 21:58:17 +0200
>> Subject: [PATCH 1/2] ui/cocoa: Defer qemu to another thread, leaving main
>> thread for the UI
> 
> <snip>
> 
> I tried both patch 1 and 2 together on Mac OS 10.12. Both qemu-system-i386 and qemu-system-ppc do not load their BIOS files. All I see is a black window each time. There is no indication of anything loading. The Machine menu doesn't populate with devices. Sorry. 
> 
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
       [not found] <mailman.21030.1541972152.1282.qemu-devel@nongnu.org>
  2018-11-13 12:12 ` Programmingkid
@ 2018-11-14  0:53 ` Programmingkid
  2018-11-14 10:15   ` Berkus Decker
  1 sibling, 1 reply; 13+ messages in thread
From: Programmingkid @ 2018-11-14  0:53 UTC (permalink / raw)
  To: QEMU Developers, Berkus Decker, Peter Maydell


> On Nov 11, 2018, at 4:35 PM, qemu-devel-request@nongnu.org wrote:
> 
> It seems that Cocoa checks are stricter on Mojave and some callbacks that worked from non-GUI thread on High Sierra are no longer working.
> 
> The fixes included here are:
> 
> * Deferring qemu_main() to another thread so that the actual main thread is reserved for the Cocoa UI; it also removes blocking from applicationDidFinishLoading: delegate. I beleive this alone caused complete UI blockage on Mojave.
> * Deferring UI-related updates in callbacks to the UI thread using invokeOnMainThread helper function. This function uses DDInvocationGrabber object courtesy of Dave Dribin, licensed under MIT license.
> Here?s relevant blog post for his code: https://www.dribin.org/dave/blog/archives/2008/05/22/invoke_on_main_thread/
> 
> NSInvocation is used here instead of plain performSelectorOnMainThread:withObject:waitUntilDone: because we want to be able to pass non-id types to the handlers.

Also I realized that the only way we could call cocoa_refresh() from performSelectorOnMainThread:withObject:waitUntilDone: would be to add it to a class. 

> These changes are ought to work on OSX 10.6, although I don?t have a machine handy to test it.
> 
> Fixes https://bugs.launchpad.net/qemu/+bug/1802684
> 
> From 8f86e30f3710d782d78dccdbe7a1564ae79220c7 Mon Sep 17 00:00:00 2001
> From: Berkus Decker <berkus+github@metta.systems>
> Date: Sun, 11 Nov 2018 21:58:17 +0200
> Subject: [PATCH 1/2] ui/cocoa: Defer qemu to another thread, leaving main
> thread for the UI

<snip>

I tried both patch 1 and 2 together on Mac OS 10.12. Both qemu-system-i386 and qemu-system-ppc do not load their BIOS files. All I see is a black window each time. There is no indication of anything loading. The Machine menu doesn't populate with devices. Sorry. 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave
       [not found] <mailman.21030.1541972152.1282.qemu-devel@nongnu.org>
@ 2018-11-13 12:12 ` Programmingkid
  2018-11-20 13:09   ` Peter Maydell
  2018-11-14  0:53 ` Programmingkid
  1 sibling, 1 reply; 13+ messages in thread
From: Programmingkid @ 2018-11-13 12:12 UTC (permalink / raw)
  To: QEMU Developers, Berkus Decker; +Cc: Peter Maydell


> On Nov 11, 2018, at 4:35 PM, qemu-devel-request@nongnu.org wrote:
> 
> It seems that Cocoa checks are stricter on Mojave and some callbacks that worked from non-GUI thread on High Sierra are no longer working.
> 
> The fixes included here are:
> 
> * Deferring qemu_main() to another thread so that the actual main thread is reserved for the Cocoa UI; it also removes blocking from applicationDidFinishLoading: delegate. I beleive this alone caused complete UI blockage on Mojave.
> * Deferring UI-related updates in callbacks to the UI thread using invokeOnMainThread helper function. This function uses DDInvocationGrabber object courtesy of Dave Dribin, licensed under MIT license.
> Here?s relevant blog post for his code: https://www.dribin.org/dave/blog/archives/2008/05/22/invoke_on_main_thread/
> 
> NSInvocation is used here instead of plain performSelectorOnMainThread:withObject:waitUntilDone: because we want to be able to pass non-id types to the handlers.

Couldn't we use [NSValue valueWithPointer:] to pass the struct instance to performSelectorOnMainThread:withObject:waitUntilDone:?

https://developer.apple.com/documentation/foundation/nsvalue/1415975-valuewithpointer?language=objc

> These changes are ought to work on OSX 10.6, although I don?t have a machine handy to test it.

I have Mac OS 10.6 available for testing. I just don't think all this code is necessary to fix this issue. Hopefully I will have the time later today to investigate this issue some more.

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2018-11-26  8:28 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-11 21:24 [Qemu-devel] [PATCH] Fix for crashes and non-responsive UI on macOS Mojave Berkus Decker
2018-11-20 12:59 ` Peter Maydell
2018-11-21 15:12   ` Peter Maydell
2018-11-22  7:32     ` Gerd Hoffmann
2018-11-25 20:43       ` Peter Maydell
2018-11-26  8:28         ` Gerd Hoffmann
     [not found] <mailman.21030.1541972152.1282.qemu-devel@nongnu.org>
2018-11-13 12:12 ` Programmingkid
2018-11-20 13:09   ` Peter Maydell
2018-11-20 13:13     ` Daniel P. Berrangé
2018-11-14  0:53 ` Programmingkid
2018-11-14 10:15   ` Berkus Decker
     [not found] <mailman.23054.1542721275.1282.qemu-devel@nongnu.org>
2018-11-21 10:53 ` Programmingkid
2018-11-21 11:05   ` Daniel P. Berrangé

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.