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>
This commit is contained in:
parent
df6322a897
commit
bab6a301c5
@ -287,8 +287,8 @@ select the fuzz target. Then, the qtest client is initialized. If the target
|
|||||||
requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized.
|
requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized.
|
||||||
Then the QGraph is walked and the QEMU cmd_line is determined and saved.
|
Then the QGraph is walked and the QEMU cmd_line is determined and saved.
|
||||||
|
|
||||||
After this, the ``vl.c:qemu_main`` is called to set up the guest. There are
|
After this, the ``vl.c:main`` is called to set up the guest. There are
|
||||||
target-specific hooks that can be called before and after qemu_main, for
|
target-specific hooks that can be called before and after main, for
|
||||||
additional setup(e.g. PCI setup, or VM snapshotting).
|
additional setup(e.g. PCI setup, or VM snapshotting).
|
||||||
|
|
||||||
``LLVMFuzzerTestOneInput``: Uses qtest/qos functions to act based on the fuzz
|
``LLVMFuzzerTestOneInput``: Uses qtest/qos functions to act based on the fuzz
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#ifndef QEMU_MAIN_H
|
#ifndef QEMU_MAIN_H
|
||||||
#define QEMU_MAIN_H
|
#define QEMU_MAIN_H
|
||||||
|
|
||||||
int qemu_main(int argc, char **argv, char **envp);
|
int qemu_default_main(void);
|
||||||
|
extern int (*qemu_main)(void);
|
||||||
|
|
||||||
#endif /* QEMU_MAIN_H */
|
#endif /* QEMU_MAIN_H */
|
||||||
|
@ -102,7 +102,7 @@ void qemu_boot_set(const char *boot_order, Error **errp);
|
|||||||
|
|
||||||
bool defaults_enabled(void);
|
bool defaults_enabled(void);
|
||||||
|
|
||||||
void qemu_init(int argc, char **argv, char **envp);
|
void qemu_init(int argc, char **argv);
|
||||||
int qemu_main_loop(void);
|
int qemu_main_loop(void);
|
||||||
void qemu_cleanup(void);
|
void qemu_cleanup(void);
|
||||||
|
|
||||||
|
@ -30,20 +30,20 @@
|
|||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int qemu_main(int argc, char **argv, char **envp)
|
int qemu_default_main(void)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
qemu_init(argc, argv, envp);
|
|
||||||
status = qemu_main_loop();
|
status = qemu_main_loop();
|
||||||
qemu_cleanup();
|
qemu_cleanup();
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_COCOA
|
int (*qemu_main)(void) = qemu_default_main;
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
return qemu_main(argc, argv, NULL);
|
qemu_init(argc, argv);
|
||||||
|
return qemu_main();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
@ -2605,7 +2605,7 @@ void qmp_x_exit_preconfig(Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_init(int argc, char **argv, char **envp)
|
void qemu_init(int argc, char **argv)
|
||||||
{
|
{
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
QemuOpts *icount_opts = NULL, *accel_opts = NULL;
|
QemuOpts *icount_opts = NULL, *accel_opts = NULL;
|
||||||
|
@ -218,7 +218,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
|
|||||||
g_free(pretty_cmd_line);
|
g_free(pretty_cmd_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_init(result.we_wordc, result.we_wordv, NULL);
|
qemu_init(result.we_wordc, result.we_wordv);
|
||||||
|
|
||||||
/* re-enable the rcu atfork, which was previously disabled in qemu_init */
|
/* re-enable the rcu atfork, which was previously disabled in qemu_init */
|
||||||
rcu_enable_atfork();
|
rcu_enable_atfork();
|
||||||
|
144
ui/cocoa.m
144
ui/cocoa.m
@ -100,13 +100,9 @@ static int cursor_hide = 1;
|
|||||||
static int left_command_key_enabled = 1;
|
static int left_command_key_enabled = 1;
|
||||||
static bool swap_opt_cmd;
|
static bool swap_opt_cmd;
|
||||||
|
|
||||||
static int gArgc;
|
|
||||||
static char **gArgv;
|
|
||||||
static bool stretch_video;
|
static bool stretch_video;
|
||||||
static NSTextField *pauseLabel;
|
static NSTextField *pauseLabel;
|
||||||
|
|
||||||
static QemuSemaphore display_init_sem;
|
|
||||||
static QemuSemaphore app_started_sem;
|
|
||||||
static bool allow_events;
|
static bool allow_events;
|
||||||
|
|
||||||
static NSInteger cbchangecount = -1;
|
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
|
* Don't try to tell QEMU about UI information in the application
|
||||||
* startup phase -- we haven't yet registered dcl with the QEMU UI
|
* 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
|
* When cocoa_display_init() does register the dcl, the UI layer
|
||||||
* will call cocoa_switch(), which will call updateUIInfo, so
|
* will call cocoa_switch(), which will call updateUIInfo, so
|
||||||
* we don't lose any information here.
|
* we don't lose any information here.
|
||||||
@ -790,16 +786,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
|
|||||||
|
|
||||||
- (bool) handleEvent:(NSEvent *)event
|
- (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 bool_with_iothread_lock(^{
|
||||||
return [self handleEventLocked:event];
|
return [self handleEventLocked:event];
|
||||||
});
|
});
|
||||||
@ -1287,8 +1273,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
|
|||||||
{
|
{
|
||||||
COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
|
COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
|
||||||
allow_events = true;
|
allow_events = true;
|
||||||
/* Tell cocoa_display_init to proceed */
|
|
||||||
qemu_sem_post(&app_started_sem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
- (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
|
* 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
|
* 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():
|
* need to start a second thread which runs the qemu_default_main():
|
||||||
*
|
|
||||||
* Initial thread: 2nd thread:
|
|
||||||
* in main():
|
* in main():
|
||||||
* create qemu-main thread
|
* in cocoa_display_init():
|
||||||
* wait on display_init semaphore
|
* assign cocoa_main to qemu_main
|
||||||
* call qemu_main()
|
* create application, menus, etc
|
||||||
* ...
|
* in cocoa_main():
|
||||||
* in cocoa_display_init():
|
* create qemu-main thread
|
||||||
* post the display_init semaphore
|
* enter OSX run loop
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void *call_qemu_main(void *opaque)
|
static void *call_qemu_main(void *opaque)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
COCOA_DEBUG("Second thread: calling qemu_main()\n");
|
COCOA_DEBUG("Second thread: calling qemu_default_main()\n");
|
||||||
status = qemu_main(gArgc, gArgv, *_NSGetEnviron());
|
qemu_mutex_lock_iothread();
|
||||||
COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n");
|
status = qemu_default_main();
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
|
COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n");
|
||||||
[cbowner release];
|
[cbowner release];
|
||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
static int cocoa_main()
|
||||||
|
{
|
||||||
QemuThread thread;
|
QemuThread thread;
|
||||||
|
|
||||||
COCOA_DEBUG("Entered main()\n");
|
COCOA_DEBUG("Entered %s()\n", __func__);
|
||||||
gArgc = argc;
|
|
||||||
gArgv = argv;
|
|
||||||
|
|
||||||
qemu_sem_init(&display_init_sem, 0);
|
|
||||||
qemu_sem_init(&app_started_sem, 0);
|
|
||||||
|
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
qemu_thread_create(&thread, "qemu_main", call_qemu_main,
|
qemu_thread_create(&thread, "qemu_main", call_qemu_main,
|
||||||
NULL, QEMU_THREAD_DETACHED);
|
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
|
// Start the main event loop
|
||||||
COCOA_DEBUG("Main thread: entering OSX run loop\n");
|
COCOA_DEBUG("Main thread: entering OSX run loop\n");
|
||||||
[NSApp run];
|
[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];
|
abort();
|
||||||
[pool release];
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2083,25 +2020,42 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
|
|||||||
|
|
||||||
static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
|
static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
|
||||||
{
|
{
|
||||||
|
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
|
COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
|
||||||
|
|
||||||
/* Tell main thread to go ahead and create the app and enter the run loop */
|
qemu_main = cocoa_main;
|
||||||
qemu_sem_post(&display_init_sem);
|
|
||||||
qemu_sem_wait(&app_started_sem);
|
// Pull this console process up to being a fully-fledged graphical
|
||||||
COCOA_DEBUG("cocoa_display_init: app start completed\n");
|
// 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 fullscreen mode is to be used */
|
||||||
if (opts->has_full_screen && opts->full_screen) {
|
if (opts->has_full_screen && opts->full_screen) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
[NSApp activateIgnoringOtherApps: YES];
|
||||||
[NSApp activateIgnoringOtherApps: YES];
|
[controller toggleFullScreen: nil];
|
||||||
[controller toggleFullScreen: nil];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) {
|
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) {
|
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);
|
qemu_event_init(&cbevent, false);
|
||||||
cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init];
|
cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init];
|
||||||
qemu_clipboard_peer_register(&cbpeer);
|
qemu_clipboard_peer_register(&cbpeer);
|
||||||
|
|
||||||
|
[pool release];
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuDisplay qemu_display_cocoa = {
|
static QemuDisplay qemu_display_cocoa = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user