[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v9 01/16] ui & main loop: Redesign of system-specific main th
From: |
Phil Dennis-Jordan |
Subject: |
Re: [PATCH v9 01/16] ui & main loop: Redesign of system-specific main thread event handling |
Date: |
Mon, 11 Nov 2024 15:13:01 +0100 |
On Mon, 11 Nov 2024 at 05:45, Akihiko Odaki <akihiko.odaki@daynix.com> wrote:
>
> On 2024/11/11 6:55, Phil Dennis-Jordan wrote:
> > macOS's Cocoa event handling must be done on the initial (main) thread
> > of the process. Furthermore, if library or application code uses
> > libdispatch, the main dispatch queue must be handling events on the main
> > thread as well.
> >
> > So far, this has affected Qemu in both the Cocoa and SDL UIs, although
> > in different ways: the Cocoa UI replaces the default qemu_main function
> > with one that spins Qemu's internal main event loop off onto a
> > background thread. SDL (which uses Cocoa internally) on the other hand
> > uses a polling approach within Qemu's main event loop. Events are
> > polled during the SDL UI's dpy_refresh callback, which happens to run
> > on the main thread by default.
> >
> > As UIs are mutually exclusive, this works OK as long as nothing else
> > needs platform-native event handling. In the next patch, a new device is
> > introduced based on the ParavirtualizedGraphics.framework in macOS.
> > This uses libdispatch internally, and only works when events are being
> > handled on the main runloop. With the current system, it works when
> > using either the Cocoa or the SDL UI. However, it does not when running
> > headless. Moreover, any attempt to install a similar scheme to the
> > Cocoa UI's main thread replacement fails when combined with the SDL
> > UI.
> >
> > This change tidies up main thread management to be more flexible.
> >
> > * The qemu_main global function pointer is a custom function for the
> > main thread, and it may now be NULL. When it is, the main thread
> > runs the main Qemu loop. This represents the traditional setup.
> > * When non-null, spawning the main Qemu event loop on a separate
> > thread is now done centrally rather than inside the Cocoa UI code.
> > * For most platforms, qemu_main is indeed NULL by default, but on
> > Darwin, it defaults to a function that runs the CFRunLoop.
> > * The Cocoa UI sets qemu_main to a function which runs the
> > NSApplication event handling runloop, as is usual for a Cocoa app.
> > * The SDL UI overrides the qemu_main function to NULL, thus
> > specifying that Qemu's main loop must run on the main
> > thread.
> > * The GTK UI also overrides the qemu_main function to NULL.
> > * For other UIs, or in the absence of UIs, the platform's default
> > behaviour is followed.
> >
> > This means that on macOS, the platform's runloop events are always
> > handled, regardless of chosen UI. The new PV graphics device will
> > thus work in all configurations. There is no functional change on other
> > operating systems.
> >
> > Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
> > Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
> > ---
> > v5:
> >
> > * Simplified the way of setting/clearing the main loop by going back
> > to setting qemu_main directly, but narrowing the scope of what it
> > needs to do, and it can now be NULL.
> >
> > v6:
> >
> > * Folded function qemu_run_default_main_on_new_thread's code into
> > main()
> > * Removed whitespace changes left over on lines near code removed
> > between v4 and v5
> >
> > v9:
> >
> > * Set qemu_main to NULL for GTK UI as well.
> >
> > include/qemu-main.h | 3 +--
> > include/qemu/typedefs.h | 1 +
> > system/main.c | 50 ++++++++++++++++++++++++++++++++++----
> > ui/cocoa.m | 54 ++++++++++-------------------------------
> > ui/gtk.c | 3 +++
> > ui/sdl2.c | 4 +++
> > 6 files changed, 67 insertions(+), 48 deletions(-)
> >
> > diff --git a/include/qemu-main.h b/include/qemu-main.h
> > index 940960a7dbc..4bd0d667edc 100644
> > --- a/include/qemu-main.h
> > +++ b/include/qemu-main.h
> > @@ -5,7 +5,6 @@
> > #ifndef QEMU_MAIN_H
> > #define QEMU_MAIN_H
> >
> > -int qemu_default_main(void);
> > -extern int (*qemu_main)(void);
> > +extern qemu_main_fn qemu_main;
> >
> > #endif /* QEMU_MAIN_H */
> > diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
> > index 3d84efcac47..b02cfe1f328 100644
> > --- a/include/qemu/typedefs.h
> > +++ b/include/qemu/typedefs.h
> > @@ -131,5 +131,6 @@ typedef struct IRQState *qemu_irq;
> > * Function types
> > */
> > typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
> > +typedef int (*qemu_main_fn)(void);
> >
> > #endif /* QEMU_TYPEDEFS_H */
> > diff --git a/system/main.c b/system/main.c
> > index 9b91d21ea8c..d9397a6d5d0 100644
> > --- a/system/main.c
> > +++ b/system/main.c
> > @@ -24,13 +24,14 @@
> >
> > #include "qemu/osdep.h"
> > #include "qemu-main.h"
> > +#include "qemu/main-loop.h"
> > #include "sysemu/sysemu.h"
> >
> > -#ifdef CONFIG_SDL
> > -#include <SDL.h>
> > +#ifdef CONFIG_DARWIN
> > +#include <CoreFoundation/CoreFoundation.h>
> > #endif
> >
> > -int qemu_default_main(void)
> > +static int qemu_default_main(void)
> > {
> > int status;
> >
> > @@ -40,10 +41,49 @@ int qemu_default_main(void)
> > return status;
> > }
> >
> > -int (*qemu_main)(void) = qemu_default_main;
> > +/*
> > + * Various macOS system libraries, including the Cocoa UI and anything
> > using
> > + * libdispatch, such as ParavirtualizedGraphics.framework, requires that
> > the
> > + * main runloop, on the main (initial) thread be running or at least
> > regularly
> > + * polled for events. A special mode is therefore supported, where the QEMU
> > + * main loop runs on a separate thread and the main thread handles the
> > + * CF/Cocoa runloop.
> > + */
> > +
> > +static void *call_qemu_default_main(void *opaque)
> > +{
> > + int status;
> > +
> > + bql_lock();
> > + status = qemu_default_main();
> > + bql_unlock();
> > +
> > + exit(status);
> > +}
> > +
> > +#ifdef CONFIG_DARWIN
> > +static int os_darwin_cfrunloop_main(void)
> > +{
> > + CFRunLoopRun();
> > + abort();
> > +}
> > +
> > +qemu_main_fn qemu_main = os_darwin_cfrunloop_main;
> > +#else
> > +qemu_main_fn qemu_main;
> > +#endif
> >
> > int main(int argc, char **argv)
> > {
> > + QemuThread main_loop_thread;
> > +
> > qemu_init(argc, argv);
> > - return qemu_main();
> > + if (qemu_main) {
> > + qemu_thread_create(&main_loop_thread, "qemu_main",
> > + call_qemu_default_main, NULL,
> > QEMU_THREAD_DETACHED);
> > + bql_unlock();
> > + return qemu_main();
> > + } else {
> > + qemu_default_main();
> > + }
> > }
> > diff --git a/ui/cocoa.m b/ui/cocoa.m
> > index 4c2dd335323..30b8920d929 100644
> > --- a/ui/cocoa.m
> > +++ b/ui/cocoa.m
> > @@ -73,6 +73,8 @@
> > int height;
> > } QEMUScreen;
> >
> > +@class QemuCocoaPasteboardTypeOwner;
> > +
> > static void cocoa_update(DisplayChangeListener *dcl,
> > int x, int y, int w, int h);
> >
> > @@ -107,6 +109,7 @@ static void cocoa_switch(DisplayChangeListener *dcl,
> > static NSInteger cbchangecount = -1;
> > static QemuClipboardInfo *cbinfo;
> > static QemuEvent cbevent;
> > +static QemuCocoaPasteboardTypeOwner *cbowner;
> >
> > // Utility functions to run specified code block with the BQL held
> > typedef void (^CodeBlock)(void);
> > @@ -1321,8 +1324,10 @@ - (void) dealloc
> > {
> > COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
> >
> > - if (cocoaView)
> > - [cocoaView release];
> > + [cocoaView release];
> > + [cbowner release];
> > + cbowner = nil;
> > +
> > [super dealloc];
> > }
> >
> > @@ -1938,8 +1943,6 @@ - (void)pasteboard:(NSPasteboard *)sender
> > provideDataForType:(NSPasteboardType)t
> >
> > @end
> >
> > -static QemuCocoaPasteboardTypeOwner *cbowner;
> > -
> > static void cocoa_clipboard_notify(Notifier *notifier, void *data);
> > static void cocoa_clipboard_request(QemuClipboardInfo *info,
> > QemuClipboardType type);
> > @@ -2002,43 +2005,8 @@ static void
> > cocoa_clipboard_request(QemuClipboardInfo *info,
> > }
> > }
> >
> > -/*
> > - * The startup process for the OSX/Cocoa UI is complicated, because
> > - * OSX insists that the UI runs on the initial main thread, and so we
> > - * need to start a second thread which runs the qemu_default_main():
> > - * in main():
> > - * in cocoa_display_init():
> > - * assign cocoa_main to qemu_main
> > - * create application, menus, etc
> > - * in cocoa_main():
> > - * create qemu-main thread
> > - * enter OSX run loop
> > - */
> > -
> > -static void *call_qemu_main(void *opaque)
> > -{
> > - int status;
> > -
> > - COCOA_DEBUG("Second thread: calling qemu_default_main()\n");
> > - bql_lock();
> > - status = qemu_default_main();
> > - bql_unlock();
> > - COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n");
> > - [cbowner release];
> > - exit(status);
> > -}
> > -
> > static int cocoa_main(void)
> > {
> > - QemuThread thread;
> > -
> > - COCOA_DEBUG("Entered %s()\n", __func__);
> > -
> > - bql_unlock();
> > - qemu_thread_create(&thread, "qemu_main", call_qemu_main,
> > - NULL, QEMU_THREAD_DETACHED);
> > -
> > - // Start the main event loop
> > COCOA_DEBUG("Main thread: entering OSX run loop\n");
> > [NSApp run];
> > COCOA_DEBUG("Main thread: left OSX run loop, which should never
> > happen\n");
> > @@ -2120,8 +2088,6 @@ static void cocoa_display_init(DisplayState *ds,
> > DisplayOptions *opts)
> >
> > COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
> >
> > - qemu_main = cocoa_main;
> > -
> > // Pull this console process up to being a fully-fledged graphical
> > // app with a menubar and Dock icon
> > ProcessSerialNumber psn = { 0, kCurrentProcess };
> > @@ -2185,6 +2151,12 @@ static void cocoa_display_init(DisplayState *ds,
> > DisplayOptions *opts)
> > qemu_clipboard_peer_register(&cbpeer);
> >
> > [pool release];
> > +
> > + /*
> > + * The Cocoa UI will run the NSApplication runloop on the main thread
> > + * rather than the default Core Foundation one.
> > + */
> > + qemu_main = cocoa_main;
> > }
> >
> > static QemuDisplay qemu_display_cocoa = {
> > diff --git a/ui/gtk.c b/ui/gtk.c
> > index bf9d3dd679a..1694146ae37 100644
> > --- a/ui/gtk.c
> > +++ b/ui/gtk.c
> > @@ -38,6 +38,7 @@
> > #include "qemu/cutils.h"
> > #include "qemu/error-report.h"
> > #include "qemu/main-loop.h"
> > +#include "qemu-main.h"
> >
> > #include "ui/console.h"
> > #include "ui/gtk.h"
> > @@ -2485,6 +2486,8 @@ static void gtk_display_init(DisplayState *ds,
> > DisplayOptions *opts)
> > #ifdef CONFIG_GTK_CLIPBOARD
> > gd_clipboard_init(s);
> > #endif /* CONFIG_GTK_CLIPBOARD */
> > +
> > + qemu_main = NULL;
>
> Add a comment that corresponds to one in: ui/sdl2.c
I've now got this:
/*
* GTK+ calls must happen on the main thread at least on some platforms,
* and on macOS the main runloop is polled via GTK+'s event handling.
* Don't allow Qemu's event loop to be moved off the main thread.
*/
It's carefully worded to only say things I know to be true - it seems
like the GTK+ UI integration doesn't actually consistently follow the
rule, which is why that UI currently doesn't work properly on macOS.
> > }
> >
> > static void early_gtk_display_init(DisplayOptions *opts)
> > diff --git a/ui/sdl2.c b/ui/sdl2.c
> > index bd4f5a9da14..44ab2762262 100644
> > --- a/ui/sdl2.c
> > +++ b/ui/sdl2.c
> > @@ -34,6 +34,7 @@
> > #include "sysemu/sysemu.h"
> > #include "ui/win32-kbd-hook.h"
> > #include "qemu/log.h"
> > +#include "qemu-main.h"
> >
> > static int sdl2_num_outputs;
> > static struct sdl2_console *sdl2_console;
> > @@ -965,6 +966,9 @@ static void sdl2_display_init(DisplayState *ds,
> > DisplayOptions *o)
> > }
> >
> > atexit(sdl_cleanup);
> > +
> > + /* SDL's event polling (in dpy_refresh) must happen on the main
> > thread. */
> > + qemu_main = NULL;
> > }
> >
> > static QemuDisplay qemu_display_sdl2 = {
>
>
- [PATCH v9 00/16] macOS PV Graphics and new vmapple machine type, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 01/16] ui & main loop: Redesign of system-specific main thread event handling, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 03/16] hw/display/apple-gfx: Adds PCI implementation, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 02/16] hw/display/apple-gfx: Introduce ParavirtualizedGraphics.Framework support, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 04/16] hw/display/apple-gfx: Adds configurable mode list, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 05/16] MAINTAINERS: Add myself as maintainer for apple-gfx, reviewer for HVF, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 06/16] hw: Add vmapple subdir, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 07/16] hw/misc/pvpanic: Add MMIO interface, Phil Dennis-Jordan, 2024/11/10
- [PATCH v9 08/16] hvf: arm: Ignore writes to CNTP_CTL_EL0, Phil Dennis-Jordan, 2024/11/10