aboutsummaryrefslogtreecommitdiff
path: root/ui/cocoa.m
diff options
context:
space:
mode:
authorAkihiko Odaki <akihiko.odaki@gmail.com>2022-08-19 22:27:54 +0900
committerGerd Hoffmann <kraxel@redhat.com>2022-09-23 14:36:33 +0200
commitbab6a301c58286229ca8fbc36728d1469f243260 (patch)
tree82377727d84f3d0a6c8348fe9073d530d1b4c3d9 /ui/cocoa.m
parentdf6322a8973b5e69bdc8931ff79d3bfe3901cab5 (diff)
ui/cocoa: Run qemu_init in the main thread
This work is based on: https://patchew.org/QEMU/20220317125534.38706-1-philippe.mathieu.daude@gmail.com/ Simplify the initialization dance by running qemu_init() in the main thread before the Cocoa event loop starts. The secondary thread only runs only qemu_main_loop() and qemu_cleanup(). This fixes a case where addRemovableDevicesMenuItems() calls qmp_query_block() while expecting the main thread to still hold the BQL. Overriding the code after calling qemu_init() is done by dynamically replacing a function pointer variable, qemu_main when initializing ui/cocoa, which unifies the static implementation of main() for builds with ui/cocoa and ones without ui/cocoa. Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com> Message-Id: <20220819132756.74641-2-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'ui/cocoa.m')
-rw-r--r--ui/cocoa.m144
1 files changed, 50 insertions, 94 deletions
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 5a8bd5dd84..660d3e0935 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -100,13 +100,9 @@ static int cursor_hide = 1;
static int left_command_key_enabled = 1;
static bool swap_opt_cmd;
-static int gArgc;
-static char **gArgv;
static bool stretch_video;
static NSTextField *pauseLabel;
-static QemuSemaphore display_init_sem;
-static QemuSemaphore app_started_sem;
static bool allow_events;
static NSInteger cbchangecount = -1;
@@ -597,7 +593,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
/*
* Don't try to tell QEMU about UI information in the application
* startup phase -- we haven't yet registered dcl with the QEMU UI
- * layer, and also trying to take the iothread lock would deadlock.
+ * layer.
* When cocoa_display_init() does register the dcl, the UI layer
* will call cocoa_switch(), which will call updateUIInfo, so
* we don't lose any information here.
@@ -790,16 +786,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
- (bool) handleEvent:(NSEvent *)event
{
- if(!allow_events) {
- /*
- * Just let OSX have all events that arrive before
- * applicationDidFinishLaunching.
- * This avoids a deadlock on the iothread lock, which cocoa_display_init()
- * will not drop until after the app_started_sem is posted. (In theory
- * there should not be any such events, but OSX Catalina now emits some.)
- */
- return false;
- }
return bool_with_iothread_lock(^{
return [self handleEventLocked:event];
});
@@ -1287,8 +1273,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
{
COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
allow_events = true;
- /* Tell cocoa_display_init to proceed */
- qemu_sem_post(&app_started_sem);
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
@@ -1919,92 +1903,45 @@ 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 vl.c qemu_main():
- *
- * Initial thread: 2nd thread:
+ * need to start a second thread which runs the qemu_default_main():
* in main():
- * create qemu-main thread
- * wait on display_init semaphore
- * call qemu_main()
- * ...
- * in cocoa_display_init():
- * post the display_init semaphore
- * wait on app_started semaphore
- * create application, menus, etc
- * enter OSX run loop
- * in applicationDidFinishLaunching:
- * post app_started semaphore
- * tell main thread to fullscreen if needed
- * [...]
- * run qemu main-loop
- *
- * We do this in two stages so that we don't do the creation of the
- * GUI application menus and so on for command line options like --help
- * where we want to just print text to stdout and exit immediately.
+ * 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_main()\n");
- status = qemu_main(gArgc, gArgv, *_NSGetEnviron());
- COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n");
+ COCOA_DEBUG("Second thread: calling qemu_default_main()\n");
+ qemu_mutex_lock_iothread();
+ status = qemu_default_main();
+ qemu_mutex_unlock_iothread();
+ COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n");
[cbowner release];
exit(status);
}
-int main (int argc, char **argv) {
+static int cocoa_main()
+{
QemuThread thread;
- COCOA_DEBUG("Entered main()\n");
- gArgc = argc;
- gArgv = argv;
-
- qemu_sem_init(&display_init_sem, 0);
- qemu_sem_init(&app_started_sem, 0);
+ COCOA_DEBUG("Entered %s()\n", __func__);
+ qemu_mutex_unlock_iothread();
qemu_thread_create(&thread, "qemu_main", call_qemu_main,
NULL, QEMU_THREAD_DETACHED);
- COCOA_DEBUG("Main thread: waiting for display_init_sem\n");
- qemu_sem_wait(&display_init_sem);
- COCOA_DEBUG("Main thread: initializing app\n");
-
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-
- // Pull this console process up to being a fully-fledged graphical
- // app with a menubar and Dock icon
- ProcessSerialNumber psn = { 0, kCurrentProcess };
- TransformProcessType(&psn, kProcessTransformToForegroundApplication);
-
- [QemuApplication sharedApplication];
-
- create_initial_menus();
-
- /*
- * Create the menu entries which depend on QEMU state (for consoles
- * and removeable devices). These make calls back into QEMU functions,
- * which is OK because at this point we know that the second thread
- * holds the iothread lock and is synchronously waiting for us to
- * finish.
- */
- add_console_menu_entries();
- addRemovableDevicesMenuItems();
-
- // Create an Application controller
- QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init];
- [NSApp setDelegate:appController];
-
// Start the main event loop
COCOA_DEBUG("Main thread: entering OSX run loop\n");
[NSApp run];
- COCOA_DEBUG("Main thread: left OSX run loop, exiting\n");
+ COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n");
- [appController release];
- [pool release];
-
- return 0;
+ abort();
}
@@ -2083,25 +2020,42 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
{
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+
COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
- /* Tell main thread to go ahead and create the app and enter the run loop */
- qemu_sem_post(&display_init_sem);
- qemu_sem_wait(&app_started_sem);
- COCOA_DEBUG("cocoa_display_init: app start completed\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 };
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+
+ [QemuApplication sharedApplication];
+
+ create_initial_menus();
+
+ /*
+ * Create the menu entries which depend on QEMU state (for consoles
+ * and removeable devices). These make calls back into QEMU functions,
+ * which is OK because at this point we know that the second thread
+ * holds the iothread lock and is synchronously waiting for us to
+ * finish.
+ */
+ add_console_menu_entries();
+ addRemovableDevicesMenuItems();
+
+ // Create an Application controller
+ QemuCocoaAppController *controller = [[QemuCocoaAppController alloc] init];
+ [NSApp setDelegate:controller];
- QemuCocoaAppController *controller = (QemuCocoaAppController *)[[NSApplication sharedApplication] delegate];
/* if fullscreen mode is to be used */
if (opts->has_full_screen && opts->full_screen) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [NSApp activateIgnoringOtherApps: YES];
- [controller toggleFullScreen: nil];
- });
+ [NSApp activateIgnoringOtherApps: YES];
+ [controller toggleFullScreen: nil];
}
if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) {
- dispatch_async(dispatch_get_main_queue(), ^{
- [controller setFullGrab: nil];
- });
+ [controller setFullGrab: nil];
}
if (opts->has_show_cursor && opts->show_cursor) {
@@ -2121,6 +2075,8 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
qemu_event_init(&cbevent, false);
cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init];
qemu_clipboard_peer_register(&cbpeer);
+
+ [pool release];
}
static QemuDisplay qemu_display_cocoa = {