summaryrefslogtreecommitdiffstats
path: root/src/test-native/bug1398/Bug1398Launcher.c
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-02-22 15:11:17 +0100
committerSven Gothel <[email protected]>2020-02-22 15:11:17 +0100
commit78b96b89a68ff35969aea83de294cd3cc1178f26 (patch)
treea1c63979d777bb52b2e365bdd3fd7e62c99c410d /src/test-native/bug1398/Bug1398Launcher.c
parentff780fc11602fb79a7ce1dcf879fdaeb865b9fa8 (diff)
Bug 1398: Crash only occurs @ -[NSOpenGLContext setView:] when using XCode 11 _and_ its default SDK 'macosx10.15'
This patch demonstrates that using the SDK 'macosx10.11' does not cause the crash @ -[NSOpenGLContext setView:]. SDK 'macosx10.15' enforces Apple's own Cargo Cult of 'main-thread' by throwing a SIGILL signal (or SIGABRT) - essentially an exception. This surely renders our code officially invalid due to this policy, i.e. we are not allowed to issue [* setView] on any non main-thread. +++ The crash occurs independently of used Java version on Java 8 - 11, as well as on JogAmp 2.3.2 - current master tip. +++ The initial remedy to issue said action on the main-thread in a blocking/wait manner has the risk to deadlock, due to 1) [NSOpenGLContext setView:] itself using a mutex (Thanks to Ken Harris's analysis) and (2) in case where we are 'thread hopping': - [main-thread] Event like 'window ready' -> kick off action on EDT-thread *blocking* - [EDT-thread] Create stuff incl OpenGLContext -> kick off setView on main-thread *blocking* This has to be further investigated. This crash finally has been reliably reproduced now.
Diffstat (limited to 'src/test-native/bug1398/Bug1398Launcher.c')
-rw-r--r--src/test-native/bug1398/Bug1398Launcher.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/test-native/bug1398/Bug1398Launcher.c b/src/test-native/bug1398/Bug1398Launcher.c
new file mode 100644
index 000000000..ee5407eff
--- /dev/null
+++ b/src/test-native/bug1398/Bug1398Launcher.c
@@ -0,0 +1,349 @@
+#include <string.h>
+#include <Cocoa/Cocoa.h>
+#include <JavaVM/jni.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
+#define DEBUG_TRACE 1
+#define TRACE(fmt, ...) \
+ do { if (DEBUG_TRACE) fprintf(err, "%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__); fflush(err); } while (0)
+
+static const char * classpath_arg_prelim = "-Djava.class.path=";
+static const char * libpath_arg_prelim = "-Djava.library.path=";
+static const char * arg_closing = "";
+
+static char * classpath_arg = NULL;
+static char * libpath_arg = NULL;
+static char * jvm_libjli_path = NULL;
+
+// JNI_CreateJavaVM
+typedef jint (JNICALL CREATEVM)(JavaVM **pvm, void **env, void *args);
+
+void die(JNIEnv *env);
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+
+@end
+
+FILE *err = NULL;
+JavaVM *jvm = NULL;
+
+static const char *JNI_CREATEJAVAVM = "JNI_CreateJavaVM";
+void *jvm_lib = NULL;
+
+void *create_vm(void)
+{
+ void *sym = NULL;
+ jvm_lib = dlopen(jvm_libjli_path, RTLD_LAZY | RTLD_GLOBAL);
+ if (jvm_lib) {
+ TRACE("Found libjli.dylib %s", jvm_libjli_path);
+ sym = dlsym(jvm_lib, JNI_CREATEJAVAVM);
+ } else {
+ TRACE("Unable to find libjli.dylib %s", jvm_libjli_path);
+ }
+ return sym;
+}
+
+static void *launchJava(void *unused)
+{
+ int k = 0;
+
+ JNIEnv *env = NULL;
+ JNINativeMethod nm_activity[20];
+ jint res;
+ jclass cls;
+ jmethodID mid;
+ jobject gui;
+ jthrowable ex;
+ // JDK > 1.5
+ JavaVMInitArgs vm_args;
+
+ TRACE("launchJava.1.1%s", "");
+ vm_args.nOptions = 9;
+ JavaVMOption options[vm_args.nOptions];
+ options[0].optionString = classpath_arg;
+ options[1].optionString = libpath_arg;
+ options[2].optionString = "-DNjogamp.debug=all";
+ options[3].optionString = "-DNjogamp.debug.NativeLibrary=true";
+ options[4].optionString = "-DNjogamp.debug.JNILibLoader=true";
+ options[5].optionString = "-DNnativewindow.debug=all";
+ options[6].optionString = "-Djogl.debug.GLContext";
+ options[7].optionString = "-Djogl.debug.GLDrawable";
+ options[8].optionString = "-Djogl.debug.GLProfile";
+
+ vm_args.version = JNI_VERSION_1_4;
+ vm_args.options = options;
+ vm_args.ignoreUnrecognized = JNI_TRUE;
+
+ TRACE("launchJava.1.2%s", "");
+ TRACE(".. using CLASSPATH %s", classpath_arg);
+ TRACE(".. using LIBPATH %s", libpath_arg);
+
+ /* Create the Java VM */
+ CREATEVM *CreateVM = create_vm();
+ TRACE("CreateVM:%lx env:%lx vm_args:%lx", (long unsigned int)CreateVM, (long unsigned int)&env, (long unsigned int)&vm_args);
+ res = CreateVM(&jvm, (void**)&env, &vm_args);
+ if (res < 0) {
+ TRACE("Can't create Java VM%s", "");
+ exit(1);
+ } else {
+ TRACE("VM Created%s", "");
+ }
+
+ TRACE("launchJava.1.3%s", "");
+ cls = (*env)->FindClass(env, "Bug1398MainClass");
+ ex = (*env)->ExceptionOccurred(env);
+ if (ex) {
+ die(env);
+ }
+
+ TRACE("launchJava.1.4%s", "");
+ mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
+ if (mid == NULL)
+ goto destroy;
+
+ TRACE("launchJava.1.5%s", "");
+ gui = (*env)->NewObject(env, cls, mid);
+ TRACE("Just passed NewObject()...%s", "");
+
+
+destroy:
+ if ((*env)->ExceptionOccurred(env)) {
+ // handle exception
+ TRACE("Exception occured...%s", "");
+ }
+
+ if (err)
+ fflush(err);
+
+ if (jvm_lib) {
+ dlclose(jvm_lib);
+ jvm_lib = NULL;
+ }
+
+ // die(env);
+
+ TRACE("launchJava.1.X%s", "");
+ return 0;
+}
+
+void show_error_dialog(JNIEnv *env, jthrowable ex)
+{
+ jclass dialogClass = (*env)->FindClass(env, "javax/swing/JOptionPane");
+ jmethodID showMsg = (*env)->GetStaticMethodID(env, dialogClass, "showMessageDialog", "(Ljava/awt/Component;Ljava/lang/Object;Ljava/lang/String;I)V");
+
+ jstring msg = (*env)->NewStringUTF(env, "\nWe be dead...\n\n");
+
+ // extract message from exception
+ jclass stringClass = (*env)->FindClass(env, "java/lang/String");
+ jclass exClass = (*env)->GetObjectClass(env, ex);
+ jmethodID exGetMessage = (*env)->GetMethodID(env, exClass, "getMessage", "()Ljava/lang/String;");
+ jmethodID concat = (*env)->GetMethodID(env, stringClass, "concat", "(Ljava/lang/String;)Ljava/lang/String;");
+ jstring exMsg = (*env)->CallObjectMethod(env, ex, exGetMessage);
+ msg = (*env)->CallObjectMethod(env, msg, concat, exMsg); // append exception message to msg
+
+ jstring title = (*env)->NewStringUTF(env, "Error");
+ (*env)->CallStaticVoidMethod(env, dialogClass, showMsg, NULL, msg, title, (jint)0);
+}
+
+void die(JNIEnv *env)
+{
+ TRACE("\n*\n*\n*\ndieing...\n%s", "");
+
+ jthrowable ex = (*env)->ExceptionOccurred(env);
+ if (ex) {
+ (*env)->ExceptionDescribe(env);
+ show_error_dialog(env, ex);
+ }
+
+ // DestroyJavaVM hangs on Windows so just exit for now
+#ifndef WIN32
+ (*jvm)->DestroyJavaVM(jvm);
+#else
+ if (jvm_lib)
+ FreeLibrary(jvm_lib);
+ if (c_lib)
+ FreeLibrary(c_lib);
+#endif
+ TRACE("VM = DEAD!%s\n", "");
+ exit(0);
+}
+
+void create_jvm_thread(void)
+{
+ pthread_t vmthread;
+
+ struct rlimit limit;
+ size_t stack_size = 0;
+ int rc = getrlimit(RLIMIT_STACK, &limit);
+
+ if (rc == 0) {
+ if (limit.rlim_cur != 0LL) {
+ stack_size = (size_t)limit.rlim_cur;
+ }
+ }
+
+ TRACE("create_jvm_thread.1.1%s", "");
+ pthread_attr_t thread_attr;
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+ if (stack_size > 0) {
+ pthread_attr_setstacksize(&thread_attr, stack_size);
+ }
+ TRACE("create_jvm_thread.1.2%s", "");
+
+ pthread_create(&vmthread, &thread_attr, launchJava, NULL);
+ pthread_attr_destroy(&thread_attr);
+ TRACE("create_jvm_thread.1.X%s", "");
+}
+
+static AppDelegate* _appDelegate;
+
+#if 0
+
+int main(int argc, const char *argv[])
+{
+ err = stderr;
+
+ for (int k = 1; k < argc; k++) {
+ TRACE("argv[%d]:%s", k, argv[k]);
+ }
+ if (argc < 2) {
+ TRACE("Usage: Bug1398Launcher %s", "[libjli.dylib path]");
+ exit(1);
+ }
+ TRACE("main.1%s", "");
+ @autoreleasepool
+ {
+ TRACE("main.1.1%s", "");
+ _appDelegate = [AppDelegate new];
+ TRACE("main.1.2%s", "");
+ [NSApplication sharedApplication];
+ TRACE("main.1.3%s", "");
+ [NSApp activateIgnoringOtherApps:YES];
+ [NSApp setDelegate:_appDelegate];
+ TRACE("main.1.5%s", "");
+
+ create_jvm_thread(argv[1]);
+ TRACE("main.1.6%s", "");
+
+ return NSApplicationMain(argc, (const char **)argv);
+ TRACE("main.1.X%s", "");
+ }
+}
+
+#else
+
+int NSApplicationMain(int argc, const char *argv[]) {
+ // [NSApplication sharedApplication];
+ // [NSBundle loadNibNamed:@"myMain" owner:NSApp];
+ // [NSApp run];
+
+ err = stderr;
+
+ fprintf(stderr, "Starting Bug1398Launcher: %s\n", argv[0]);
+
+ const int arg_closing_len = strlen(arg_closing);
+
+ for (int k = 1; k < argc; k++) {
+ if( !strcmp("-classpath", argv[k]) && k+1 < argc ) {
+ const int classpath_arg_prelim_len = strlen(classpath_arg_prelim);
+ const int classpath_len = strlen(argv[++k]);
+ classpath_arg = calloc(classpath_len + classpath_arg_prelim_len + arg_closing_len + 1, 1);
+ strncpy(classpath_arg, classpath_arg_prelim, classpath_arg_prelim_len+1);
+ strncpy(classpath_arg+classpath_arg_prelim_len, argv[k], classpath_len+1);
+ strncpy(classpath_arg+classpath_arg_prelim_len+classpath_len, arg_closing, arg_closing_len+1);
+ TRACE("argv[%d]: classpath arg %s", k, classpath_arg);
+ } else if( !strcmp("-libpath", argv[k]) && k+1 < argc ) {
+ const int libpath_arg_prelim_len = strlen(libpath_arg_prelim);
+ const int libpath_len = strlen(argv[++k]);
+ libpath_arg = calloc(libpath_len + libpath_arg_prelim_len + arg_closing_len + 1, 1);
+ strncpy(libpath_arg, libpath_arg_prelim, libpath_arg_prelim_len+1);
+ strncpy(libpath_arg+libpath_arg_prelim_len, argv[k], libpath_len+1);
+ strncpy(libpath_arg+libpath_arg_prelim_len+libpath_len, arg_closing, arg_closing_len+1);
+ TRACE("argv[%d]: libpath arg %s", k, libpath_arg);
+ } else if( !strcmp("-jvmlibjli", argv[k]) && k+1 < argc ) {
+ const int len = strlen(argv[++k]);
+ jvm_libjli_path = calloc(len + 1, 1);
+ strncpy(jvm_libjli_path, argv[k], len+1);
+ TRACE("argv[%d]: jvmlibjli %s", k, jvm_libjli_path);
+ } else {
+ TRACE("argv[%d]:%s", k, argv[k]);
+ }
+ }
+ if ( NULL == classpath_arg || NULL == libpath_arg || NULL == jvm_libjli_path ) {
+ TRACE("Usage: %s -classpath CLASSPATH -libpath LIBPATH -jvmlibjli libjli.dylib", argv[0]);
+ exit(1);
+ }
+ TRACE("main.1%s", "");
+ @autoreleasepool
+ {
+ TRACE("main.1.1%s", "");
+ _appDelegate = [AppDelegate new];
+ TRACE("main.1.2%s", "");
+ [NSApplication sharedApplication];
+ TRACE("main.1.3%s", "");
+ [NSApp activateIgnoringOtherApps:YES];
+ [NSApp setDelegate:_appDelegate];
+ TRACE("main.1.5%s", "");
+
+ create_jvm_thread();
+ TRACE("main.1.6%s", "");
+
+ [NSApp run];
+ // return NSApplicationMain(argc, (const char **)argv);
+ TRACE("main.1.X%s", "");
+ }
+ return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ return NSApplicationMain(argc, (const char **)argv);
+}
+
+#endif
+
+@interface AppDelegate ()
+
+@property (strong) IBOutlet NSWindow *window;
+
+@end
+
+@implementation AppDelegate
+
+-(id) init
+{
+ self = [super init];
+ NSLog(@"init");
+ return self;
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+
+ NSLog(@"App starting...");
+
+ //Create a new Block Operation and add it to the Operation Queue
+ NSOperationQueue *operationQueue = [NSOperationQueue new];
+
+ NSBlockOperation *startUpCompletionOperation = [NSBlockOperation blockOperationWithBlock:^{
+ //The startup Object has been loaded now close the splash screen
+ //This the completion block operation
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSLog(@"startUpCompletionOperation main thread? ANS - %@",[NSThread isMainThread]? @"YES":@"NO");
+// launchJava((void *)"jre/lib/jli/libjli.dylib");
+ }];
+ }];
+
+ NSBlockOperation *startUpOperation = [NSBlockOperation blockOperationWithBlock:^{
+ // wait for everything to load and JVM to power up
+ sleep(3); // wait for a bit for NewObject to complete
+ }];
+
+ [startUpCompletionOperation addDependency:startUpOperation];
+ [operationQueue addOperation:startUpCompletionOperation];
+ [operationQueue addOperation:startUpOperation];
+}
+
+@end