diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 5a8a3500951..7fb2a67dcb3 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -2537,6 +2537,8 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag utils.monodroid_runtime_invoke (domain, register_jni_natives, nullptr, args, nullptr); #else // ndef NET + mono_jit_thread_attach (nullptr); // There's just one domain in .net + #if !defined (ANDROID) mono_runtime_invoke (register_jni_natives, nullptr, args, nullptr); #else diff --git a/tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs b/tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs index 11ac6e04b18..a1efbb26591 100644 --- a/tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs +++ b/tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs @@ -32,11 +32,31 @@ public void TestMyPaintColor () } } + [DllImport ("reuse-threads")] + static extern int rt_register_type_on_new_thread (string java_type_namem, IntPtr class_loader); + delegate void CB (IntPtr jnienv, IntPtr java_instance); [DllImport ("reuse-threads")] static extern int rt_invoke_callback_on_new_thread (CB cb); + [Test] + public void RegisterTypeOnNewNativeThread () + { + Java.Lang.JavaSystem.LoadLibrary ("reuse-threads"); + int ret = rt_register_type_on_new_thread ("from.NewThreadOne", Application.Context.ClassLoader.Handle); + Assert.AreEqual (0, ret, $"Java type registration on a new thread failed with code {ret}"); + } + + [Test] + public void RegisterTypeOnNewJavaThread () + { + var thread = new MyRegistrationThread (); + thread.Start (); + thread.Join (5000); + Assert.AreNotEqual (null, thread.Instance, "Failed to register instance of a class on new thread"); + } + [Test] public void ThreadReuse () { @@ -434,6 +454,24 @@ public void DoNotLeakWeakReferences () } } + [Register ("from/NewThreadOne")] + class RegisterMeOnNewThreadOne : Java.Lang.Object + {} + + [Register ("from/NewThreadTwo")] + class RegisterMeOnNewThreadTwo : Java.Lang.Object + {} + + class MyRegistrationThread : Java.Lang.Thread + { + public RegisterMeOnNewThreadTwo Instance { get; private set; } + + public override void Run () + { + Instance = new RegisterMeOnNewThreadTwo (); + } + } + class MyCb : Java.Lang.Object, Java.Lang.IRunnable { public void Run () { diff --git a/tests/Mono.Android-Tests/jni/reuse-threads.c b/tests/Mono.Android-Tests/jni/reuse-threads.c index bdc1f415694..07223a60f37 100644 --- a/tests/Mono.Android-Tests/jni/reuse-threads.c +++ b/tests/Mono.Android-Tests/jni/reuse-threads.c @@ -81,6 +81,12 @@ #include #include +typedef struct +{ + const char *java_type_name; + jobject class_loader; +} RegisterFromThreadContext; + typedef void (*CB)(JNIEnv *env, jobject self); static JavaVM *gvm; @@ -113,9 +119,9 @@ _get_env (const char *where) } static jobject -_create_java_instance (JNIEnv *env) +_create_java_instance (JNIEnv *env, const char *class_name) { - jclass Object_class = (*env)->FindClass (env, "java/lang/Object"); + jclass Object_class = (*env)->FindClass (env, class_name); jmethodID Object_ctor = (*env)->GetMethodID (env, Object_class, "", "()V"); jobject instance = (*env)->NewObject (env, Object_class, Object_ctor); @@ -154,7 +160,7 @@ _call_cb_from_new_thread (void *cb) } /* 5: Execution of T enters managed code... */ - jobject instance = _create_java_instance (env); + jobject instance = _create_java_instance (env, "java/lang/Object"); _cb (env, instance); return NULL; @@ -200,3 +206,116 @@ rt_invoke_callback_on_new_thread (CB cb) return 0; } +/* We return -2 for errors, because -1 is reserved for the pthreads PTHREAD_CANCELED special value, indicating that the + * thread was canceled. */ +static int +_register_type_from_new_thread (void *data) +{ + RegisterFromThreadContext *context = (RegisterFromThreadContext*)data; + + if (context == NULL) { + return -100; + } + + JNIEnv *env = _get_env ("_register_type_from_new_thread"); + + if ((*env)->PushLocalFrame (env, 4) < 0) { + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: unable to create a local reference frame!"); + + if ((*env)->ExceptionOccurred (env)) { + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + } + + return -101; + } + + int ret = 0; + jclass ClassLoader_class = (*env)->FindClass (env, "java/lang/ClassLoader"); + if (ClassLoader_class == NULL) { + ret = -102; + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: unable to find the 'java/lang/ClassLoader' class!"); + goto cleanup; + } + + jmethodID loadClass = (*env)->GetMethodID (env, ClassLoader_class, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + if (loadClass == NULL) { + ret = -103; + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: unable to get id of method 'loadClass' in the 'java/lang/ClassLoader' class!"); + goto cleanup; + } + + jstring klass_name = (*env)->NewStringUTF (env, context->java_type_name); + jobject loaded_class = (*env)->CallObjectMethod (env, context->class_loader, loadClass, klass_name); + + if ((*env)->ExceptionOccurred (env) != NULL) { + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: class '%s' cannot be loaded, Java exception thrown!", context->java_type_name); + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + ret = -104; + goto cleanup; + } + + if (loaded_class == NULL) { + ret = -105; + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: 'java/lang/ClassLoader' wasn't able to load the '%s' class!", context->java_type_name); + goto cleanup; + } + + jmethodID Object_ctor = (*env)->GetMethodID (env, loaded_class, "", "()V"); + if (Object_ctor == NULL) { + ret = -106; + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: unable to find the '%s' class constructor!", context->java_type_name); + goto cleanup; + } + + jobject instance = (*env)->NewObject (env, loaded_class, Object_ctor); + + if ((*env)->ExceptionOccurred (env) != NULL || instance == NULL) { + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: instance of class '%s' wasn't created!", context->java_type_name); + (*env)->ExceptionDescribe (env); + (*env)->ExceptionClear (env); + ret = -107; + } + + if (instance == NULL) { + ret = -108; + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "FAILURE: unable to create instance of the '%s' class!", context->java_type_name); + } + + cleanup: + (*env)->PopLocalFrame (env, NULL); + + return ret; +} + +JNIEXPORT int JNICALL +rt_register_type_on_new_thread (const char *java_type_name, jobject class_loader) +{ + JNIEnv *env = _get_env ("rt_register_type_on_new_thread"); + pthread_t t; + RegisterFromThreadContext context = { + java_type_name, + class_loader, + }; + + int r = pthread_create (&t, NULL, _register_type_from_new_thread, &context); + + if (r) { + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "RegisterOnNewThread: pthread_create() failed! %i: %s", r, strerror (r)); + return -200; + } + + void *tr; + if (pthread_join (t, &tr) != 0) { + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "RegisterOnNewThread: pthread_join() failed! %i: %s", r, strerror (r)); + return -201; + } + + if ((int)tr == -1 /* PTHREAD_CANCELED - not defined in bionic */) { + __android_log_print (ANDROID_LOG_INFO, "XA/RuntimeTest", "RegisterOnNewThread: worker thread was canceled"); + return -202; + } + + return (int)tr; +}