118118#define IS_VM_LIBRARY_PATH_ARG (ARG ) STARTS_WITH(ARG, VM_LIBRARY_PATH_ARG_PREFIX)
119119#define IS_VM_STACK_SIZE_ARG (ARG ) STARTS_WITH(ARG, VM_STACK_SIZE_ARG_PREFIX)
120120#define IS_VM_ARG_FILE_ARG (ARG ) STARTS_WITH(ARG, VM_ARG_FILE_ARG_PREFIX)
121+ #define IS_VM_START_ON_FIRST_THREAD (ARG ) (ARG == " --vm.XstartOnFirstThread" )
121122
122123#define NMT_ARG_NAME " XX:NativeMemoryTracking"
123124#define NMT_ENV_NAME " NMT_LEVEL_"
@@ -364,7 +365,8 @@ static void parse_vm_option(
364365 std::ostringstream *cp,
365366 std::ostringstream *modulePath,
366367 std::ostringstream *libraryPath,
367- size_t * stack_size,
368+ size_t *stack_size,
369+ bool *startOnFirstThread,
368370 std::string_view option) {
369371 if (IS_VM_CP_ARG (option)) {
370372 *cp << CP_SEP_STR << option.substr (VM_CP_ARG_OFFSET);
@@ -379,6 +381,10 @@ static void parse_vm_option(
379381 } else if (IS_VM_ARG_FILE_ARG (option)) {
380382 std::string arg_file (option.substr (VM_ARG_FILE_ARG_OFFSET));
381383 expand_vm_arg_file (arg_file.c_str (), vmArgs, cp, modulePath, libraryPath, stack_size);
384+ #if defined (__APPLE__)
385+ } else if (IS_VM_START_ON_FIRST_THREAD (option)) {
386+ *startOnFirstThread = true ;
387+ #endif
382388 } else if (IS_VM_ARG (option)) {
383389 if (IS_VM_STACK_SIZE_ARG (option)) {
384390 *stack_size = parse_size (option.substr (VM_STACK_SIZE_ARG_OFFSET));
@@ -555,13 +561,14 @@ struct MainThreadArgs {
555561 bool jvmMode;
556562 std::string libPath;
557563 size_t stack_size{};
564+ bool startOnFirstThread;
558565 std::vector<std::string> vmArgs;
559566 std::vector<std::string> optionVarsArgs;
560567};
561568
562569/* parse the VM arguments that should be passed to JNI_CreateJavaVM */
563570static void parse_vm_options (struct MainThreadArgs & parsedArgs) {
564- auto & [argc, argv, exeDir, jvmMode, _, stack_size, vmArgs, optionVarsArgs] = parsedArgs;
571+ auto & [argc, argv, exeDir, jvmMode, _, stack_size, startOnFirstThread, vmArgs, optionVarsArgs] = parsedArgs;
565572
566573 /* check if vm args have been set on relaunch already */
567574 int vmArgCount = 0 ;
@@ -715,7 +722,7 @@ static void parse_vm_options(struct MainThreadArgs& parsedArgs) {
715722 const char *launcherDefaultVmArgs[] = LAUNCHER_DEFAULT_VM_ARGS;
716723 for (int i = 0 ; i < sizeof (launcherDefaultVmArgs)/sizeof (char *); i++) {
717724 if (IS_VM_ARG (std::string (launcherDefaultVmArgs[i]))) {
718- parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, launcherDefaultVmArgs[i]);
725+ parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, &startOnFirstThread, launcherDefaultVmArgs[i]);
719726 }
720727 }
721728 #endif
@@ -724,7 +731,7 @@ static void parse_vm_options(struct MainThreadArgs& parsedArgs) {
724731 if (!relaunch) {
725732 /* handle CLI arguments */
726733 for (int i = 1 ; i < argc; i++) {
727- parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, argv[i]);
734+ parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, &startOnFirstThread, argv[i]);
728735 }
729736
730737 /* handle optional vm args from LanguageLibraryConfig.option_vars */
@@ -745,12 +752,12 @@ static void parse_vm_options(struct MainThreadArgs& parsedArgs) {
745752 while ((next = optionLine.find (" " , last)) != std::string::npos) {
746753 option = optionLine.substr (last, next-last);
747754 optionVarsArgs.push_back (option);
748- parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, option);
755+ parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, &startOnFirstThread, option);
749756 last = next + 1 ;
750757 };
751758 option = optionLine.substr (last);
752759 optionVarsArgs.push_back (option);
753- parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, option);
760+ parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, &startOnFirstThread, option);
754761 }
755762 #endif
756763 } else {
@@ -769,7 +776,7 @@ static void parse_vm_options(struct MainThreadArgs& parsedArgs) {
769776 std::cerr << " VM arguments specified: " << vmArgCount << " but argument " << i << " missing" << std::endl;
770777 break ;
771778 }
772- parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, cur);
779+ parse_vm_option (&vmArgs, &cp, &modulePath, &libraryPath, &stack_size, &startOnFirstThread, cur);
773780 /* clean up env variable */
774781 setenv (envKey, " " );
775782 }
@@ -865,7 +872,7 @@ int main(int argc, char *argv[]) {
865872
866873
867874 /* parse VM args */
868- struct MainThreadArgs parsedArgs{argc, argv, exeDir, jvmMode, libPath, 0 };
875+ struct MainThreadArgs parsedArgs{argc, argv, exeDir, jvmMode, libPath, 0 , false };
869876 parse_vm_options (parsedArgs);
870877 size_t stack_size = parsedArgs.stack_size ;
871878
@@ -883,10 +890,12 @@ int main(int argc, char *argv[]) {
883890 size_t main_thread_stack_size = current_thread_stack_size ();
884891 bool use_new_thread = stack_size > main_thread_stack_size;
885892#if defined (__APPLE__)
886- /* On macOS, always create a dedicated "main" thread for the JVM.
893+ /* On macOS, default to creating a dedicated "main" thread for the JVM.
887894 * The actual main thread must run the UI event loop (needed for AWT).
895+ * It can be overridden with -XstartOnFirstThread, this is needed to
896+ * use other UI frameworks that *do* need to run on the main thread.
888897 */
889- use_new_thread = true ;
898+ use_new_thread = !parsedArgs. startOnFirstThread ;
890899
891900 if (jvmMode) {
892901 if (!load_jli_lib (exeDir)) {
@@ -965,7 +974,7 @@ int main(int argc, char *argv[]) {
965974}
966975
967976static int jvm_main_thread (struct MainThreadArgs & parsedArgs) {
968- auto & [argc, argv, _, jvmMode, libPath, stack_size, vmArgs, optionVarsArgs] = parsedArgs;
977+ auto & [argc, argv, _, jvmMode, libPath, stack_size, startOnFirstThread, vmArgs, optionVarsArgs] = parsedArgs;
969978
970979 /* load VM library - after parsing arguments s.t. NMT
971980 * tracking environment variables are already set */
0 commit comments