Skip to content

Commit

Permalink
[WIP] JNIEnv.Initialize optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
grendello committed Oct 2, 2019
1 parent 84aca4d commit 6303191
Show file tree
Hide file tree
Showing 14 changed files with 418 additions and 221 deletions.
122 changes: 39 additions & 83 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ struct JnienvInitializeArgs {
public int localRefsAreIndirect;
public int grefGcThreshold;
public IntPtr grefIGCUserPeer;
public int isRunningOnDesktop;
public byte isRunningOnDesktop;
public byte brokenExceptionTransitions;
public PackageNamingPolicy packageNamingPolicy;
}

public static partial class JNIEnv {
Expand All @@ -49,7 +51,7 @@ public static partial class JNIEnv {

internal static int gref_gc_threshold;
internal static IntPtr mid_Class_getName;

internal static bool PropagateExceptions;
static UncaughtExceptionHandler defaultUncaughtExceptionHandler;

Expand All @@ -64,7 +66,7 @@ public static partial class JNIEnv {
[ThreadStatic] static JniNativeInterfaceInvoker env;

static JniNativeInterfaceInvoker Env {
get {
get {
if (Handle == IntPtr.Zero) // Forces thread attach if necessary.
throw new Exception ("JNIEnv handle is NULL");
return env;
Expand Down Expand Up @@ -115,53 +117,42 @@ public static void CheckHandle (IntPtr jnienv)
static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)
{
string typeName = new string ((char*) typeName_ptr, 0, typeName_len);

var __start = new DateTime ();
if (Logger.LogTiming) {
__start = DateTime.UtcNow;
Logger.Log (LogLevel.Info,
"monodroid-timing",
"JNIEnv.RegisterJniNatives (\"" + typeName + "\", 0x" + jniClass.ToString ("x") + ") start: " + (__start - new DateTime (1970, 1, 1)).TotalMilliseconds);
}

Type type = Type.GetType (typeName);
if (type == null) {
Logger.Log (LogLevel.Error, "MonoDroid",
"Could not load type '" + typeName + "'. Skipping JNI registration of type '" +
"Could not load type '" + typeName + "'. Skipping JNI registration of type '" +
Java.Interop.TypeManager.GetClassName (jniClass) + "'.");
return;
}

var className = Java.Interop.TypeManager.GetClassName (jniClass);
var className = Java.Interop.TypeManager.GetClassName (jniClass); // TODO: <- replace contents of GetClassName with p/invokes
TypeManager.RegisterType (className, type);

JniType jniType = null;
JniType.GetCachedJniType (ref jniType, className);

androidRuntime.TypeManager.RegisterNativeMembers (jniType, type, methods_ptr == IntPtr.Zero ? null : new string ((char*) methods_ptr, 0, methods_len));

if (Logger.LogTiming) {
var __end = DateTime.UtcNow;
Logger.Log (LogLevel.Info,
"monodroid-timing",
"JNIEnv.RegisterJniNatives total time: " + (__end - new DateTime (1970, 1, 1)).TotalMilliseconds + " [elapsed: " + (__end - __start).TotalMilliseconds + " ms]");
}
}

[DllImport ("__Internal")]
extern static void monodroid_log (LogLevel level, LogCategories category, string message);

[DllImport ("__Internal")]
extern static IntPtr monodroid_timing_start (string message);

[DllImport ("__Internal")]
extern static void monodroid_timing_stop (IntPtr sequence, string message);

internal static unsafe void Initialize (JnienvInitializeArgs* args)
{
Logger.Categories = (LogCategories) args->logCategories;

Stopwatch stopper = null;
long elapsed, totalElapsed = 0;
if (Logger.LogTiming) {
stopper = new Stopwatch ();
stopper.Start ();
Logger.Log (LogLevel.Info, "monodroid-timing", "JNIEnv.Initialize start");
elapsed = stopper.ElapsedMilliseconds;
totalElapsed += elapsed;
Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: Logger JIT/etc. time: elapsed {elapsed} ms]");
stopper.Restart ();
// TODO: access to args fields (some of them - analysis needed) should be lazy - p/invokes, with locking or atomic operations or TLS (test times!)

bool logTiming = (args->logCategories & (uint)LogCategories.Timing) != 0;
IntPtr total_timing_sequence = IntPtr.Zero;
IntPtr partial_timing_sequence = IntPtr.Zero;
if (logTiming) {
total_timing_sequence = monodroid_timing_start ("JNIEnv.Initialize start");
partial_timing_sequence = monodroid_timing_start (null);
}

gref_gc_threshold = args->grefGcThreshold;
Expand All @@ -170,13 +161,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)

java_vm = args->javaVm;

#if !JAVA_INTEROP
handle = args->env;
env = CreateNativeInterface ();

invoke_iface = (JNIInvokeInterface) Marshal.PtrToStructure (Marshal.ReadIntPtr (java_vm), typeof (JNIInvokeInterface));
#endif // !JAVA_INTEROP

version = args->version;

androidSdkVersion = args->androidSdkVersion;
Expand All @@ -193,60 +177,34 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)

Mono.SystemDependencyProvider.Initialize ();

#if JAVA_INTEROP
// TODO: env, javaVm, grefLoader, Loader_loadClass should all be obtained by AndroidRuntime constructor with p/invokes
// TODO: consider removing AndroidRuntime instantiation here
androidRuntime = new AndroidRuntime (args->env, args->javaVm, androidSdkVersion > 10, args->grefLoader, args->Loader_loadClass);
#endif // JAVA_INTEROP

if (Logger.LogTiming) {
elapsed = stopper.ElapsedMilliseconds;
totalElapsed += elapsed;
Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: managed runtime init time: elapsed {elapsed} ms]");
stopper.Restart ();
if (logTiming) {
monodroid_timing_stop (partial_timing_sequence, "JNIEnv.Initialize: managed runtime init time");
partial_timing_sequence = monodroid_timing_start (null);
var _ = Java.Interop.TypeManager.jniToManaged;
elapsed = stopper.ElapsedMilliseconds;
totalElapsed += elapsed;
Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: TypeManager init time: elapsed {elapsed} ms]");
monodroid_timing_stop (partial_timing_sequence, "JNIEnv.Initialize: TypeManager init time");
}

AllocObjectSupported = androidSdkVersion > 10;
IsRunningOnDesktop = Convert.ToBoolean (args->isRunningOnDesktop);
IsRunningOnDesktop = args->isRunningOnDesktop == 1;

Java.Interop.Runtime.grefIGCUserPeer_class = args->grefIGCUserPeer;

#if BROKEN_EXCEPTION_TRANSITIONS // XA < 5.0
var propagate =
Environment.GetEnvironmentVariable ("__XA_PROPAGATE_EXCEPTIONS__") ??
Environment.GetEnvironmentVariable ("__XA_PROPOGATE_EXCEPTIONS__");
if (!string.IsNullOrEmpty (propagate)) {
bool.TryParse (propagate, out PropagateExceptions);
}
if (PropagateExceptions) {
Logger.Log (LogLevel.Info,
"monodroid",
"Enabling managed-to-java exception propagation.");
}
#else
PropagateExceptions = true;
var brokenExceptionTransitions = Environment.GetEnvironmentVariable ("XA_BROKEN_EXCEPTION_TRANSITIONS");
bool brokenTransitions;
if (!string.IsNullOrEmpty (brokenExceptionTransitions) && bool.TryParse (brokenExceptionTransitions, out brokenTransitions)) {
PropagateExceptions = !brokenTransitions;
}
#endif
PropagateExceptions = args->brokenExceptionTransitions == 0;

if (PropagateExceptions) {
defaultUncaughtExceptionHandler = new UncaughtExceptionHandler (Java.Lang.Thread.DefaultUncaughtExceptionHandler);
if (!IsRunningOnDesktop)
Java.Lang.Thread.DefaultUncaughtExceptionHandler = defaultUncaughtExceptionHandler;
}

var packageNamingPolicy = Environment.GetEnvironmentVariable ("__XA_PACKAGE_NAMING_POLICY__");
if (Enum.TryParse (packageNamingPolicy, out PackageNamingPolicy pnp)) {
JavaNativeTypeManager.PackageNamingPolicy = pnp;
}
JavaNativeTypeManager.PackageNamingPolicy = (PackageNamingPolicy)args->packageNamingPolicy;

if (Logger.LogTiming) {
totalElapsed += stopper.ElapsedMilliseconds;
Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize end: elapsed {totalElapsed} ms");
if (logTiming) {
monodroid_timing_stop (total_timing_sequence, "JNIEnv.Initialize end");
}
}

Expand Down Expand Up @@ -888,7 +846,7 @@ public static string GetCharSequence (IntPtr jobject, JniHandleOwnership transfe
var r = JniEnvironment.Object.ToString (new JniObjectReference (jobject));
return JniEnvironment.Strings.ToString (ref r, JniObjectReferenceOptions.CopyAndDispose);
#else // !JAVA_INTEROP
IntPtr str = LogCreateLocalRef (Env.CallObjectMethod (Handle, jobject, Java.Lang.Class.CharSequence_toString));
IntPtr str = LogCreateLocalRef (Env.CallObjectMethod (Handle, jobject, Java.Lang.Class.CharSequence_toString));
try {
return GetString (str, JniHandleOwnership.TransferLocalRef);
} finally {
Expand Down Expand Up @@ -2031,7 +1989,7 @@ struct JNIInvokeInterface {
public IntPtr reserved0;
public IntPtr reserved1;
public IntPtr reserved2;

public IntPtr DestroyJavaVM; // jint (*DestroyJavaVM)(JavaVM*);
public AttachCurrentThreadDelegate AttachCurrentThread;
public DetachCurrentThreadDelegate DetachCurrentThread;
Expand All @@ -2051,7 +2009,7 @@ public JNINativeMethod (string name, string sig, Delegate func)
Sig = sig;
Func = func;
}
}
}
#endif // !JAVA_INTEROP

#if ANDROID_8
Expand Down Expand Up @@ -2081,5 +2039,3 @@ internal static int AndroidBitmap_unlockPixels (IntPtr jbitmap)
#endif // ANDROID_8
}
}


33 changes: 23 additions & 10 deletions src/Mono.Android/Android.Runtime/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@

namespace Android.Runtime {
internal static class Logger {
internal static LogCategories Categories;
static object initLock = new object ();
static LogCategories logCategories;
static bool hasNoLibLog;

internal static LogCategories Categories => logCategories;

internal static bool LogAssembly {
get {return (Categories & LogCategories.Assembly) != 0;}
}
Expand Down Expand Up @@ -58,18 +61,28 @@ public static void Log (LogLevel level, string appname, string log) {
System.Console.WriteLine ("[{0}] {1}: {2}", level, appname, line);
}
}

[DllImport ("__Internal")]
extern static uint monodroid_get_log_categories ();

static Logger ()
{
lock (initLock) {
logCategories = (LogCategories) monodroid_get_log_categories ();
}
}
}

internal enum LogLevel {
Unknown,
Default,
Verbose,
Debug,
Info,
Warn,
Error,
Fatal,
Silent
Unknown = 0x00,
Default = 0x01,
Verbose = 0x02,
Debug = 0x03,
Info = 0x04,
Warn = 0x05,
Error = 0x06,
Fatal = 0x07,
Silent = 0x08
}

// Keep in sync with the LogCategories enum in
Expand Down
28 changes: 5 additions & 23 deletions src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Java.Interop {
public static partial class TypeManager {

// Make this internal so that JNIEnv.Initialize can trigger the static
// constructor so that JNIEnv.RegisterJNINatives() doesn't include
// constructor so that JNIEnv.RegisterJNINatives() doesn't include
// the static constructor execution.

// Lock on jniToManaged before accessing EITHER jniToManaged or managedToJni.
Expand All @@ -21,26 +21,6 @@ public static partial class TypeManager {

internal static IntPtr id_Class_getName;

static TypeManager ()
{
var start = new DateTime ();
if (Logger.LogTiming) {
start = DateTime.UtcNow;
Logger.Log (LogLevel.Info,
"monodroid-timing",
"TypeManager.cctor start: " + (start - new DateTime (1970, 1, 1)).TotalMilliseconds);
}

__TypeRegistrations.RegisterPackages ();

if (Logger.LogTiming) {
var end = DateTime.UtcNow;
Logger.Log (LogLevel.Info,
"monodroid-timing",
"TypeManager.cctor time: " + (end - new DateTime (1970, 1, 1)).TotalMilliseconds + " [elapsed: " + (end - start).TotalMilliseconds + " ms]");
}
}

internal static string GetClassName (IntPtr class_ptr)
{
return JNIEnv.GetString (JNIEnv.CallObjectMethod (class_ptr, JNIEnv.mid_Class_getName), JniHandleOwnership.TransferLocalRef).Replace (".", "/");
Expand Down Expand Up @@ -213,6 +193,8 @@ internal static Type GetJavaToManagedType (string class_name)
return null;
}

__TypeRegistrations.RegisterPackages ();

var type = (Type) null;
int ls = class_name.LastIndexOf ('/');
var package = ls >= 0 ? class_name.Substring (0, ls) : "";
Expand Down Expand Up @@ -256,7 +238,7 @@ internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership tr
class_name = GetClassName (class_ptr);
}
}

JNIEnv.DeleteLocalRef (class_ptr);

if (type == null) {
Expand All @@ -269,7 +251,7 @@ internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership tr

if (targetType != null && !targetType.IsAssignableFrom (type))
type = targetType;

if (type.IsInterface || type.IsAbstract) {
var invokerType = JavaObjectExtensions.GetHelperType (type, "Invoker");
if (invokerType == null)
Expand Down
Loading

0 comments on commit 6303191

Please sign in to comment.