Skip to content

Commit c9af66c

Browse files
[Android] Decouple runtime initialization and entry point execution for Android sample (#111742)
By decoupling the initialization of runtime and execution of entry point, we can run the runtime initialization synchronously, forcing the onCreate callback to report the Displayed <name> for user 0: <time> after the runtime is fully initialized. The execution of the entry point is still performed asynchronously to prevent the freeze of main thread for strenuous executables. --------- Co-authored-by: Ivan Povazan <[email protected]>
1 parent d2b9703 commit c9af66c

File tree

5 files changed

+309
-107
lines changed

5 files changed

+309
-107
lines changed

Diff for: src/tasks/AndroidAppBuilder/Templates/MainActivity.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ protected void onCreate(Bundle savedInstanceState)
4141
}
4242

4343
final Activity ctx = this;
44+
MonoRunner.initializeRuntime(entryPointLibName, ctx);
4445
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
4546
@Override
4647
public void run() {
47-
int retcode = MonoRunner.initialize(entryPointLibName, new String[0], ctx);
48+
int retcode = MonoRunner.executeEntryPoint(entryPointLibName, new String[0]);
4849
textView.setText("Mono Runtime returned: " + retcode);
50+
ctx.reportFullyDrawn();
4951
}
5052
}, 1000);
5153
}

Diff for: src/tasks/AndroidAppBuilder/Templates/MonoRunner.java

+35-6
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public void onCreate(Bundle arguments) {
7474
start();
7575
}
7676

77-
public static int initialize(String entryPointLibName, String[] args, Context context) {
77+
public static void initializeRuntime(String entryPointLibName, Context context) {
7878
String filesDir = context.getFilesDir().getAbsolutePath();
7979
String cacheDir = context.getCacheDir().getAbsolutePath();
8080

@@ -90,9 +90,24 @@ public static int initialize(String entryPointLibName, String[] args, Context co
9090
// unzip libs and test files to filesDir
9191
unzipAssets(context, filesDir, "assets.zip");
9292

93-
Log.i("DOTNET", "MonoRunner initialize,, entryPointLibName=" + entryPointLibName);
93+
// set environment variables
94+
setEnv("HOME", filesDir);
95+
setEnv("TMPDIR", cacheDir);
96+
setEnv("TEST_RESULTS_DIR", testResultsDir);
97+
98+
Log.i("DOTNET", "MonoRunner initializeRuntime, entryPointLibName=" + entryPointLibName);
9499
int localDateTimeOffset = getLocalDateTimeOffset();
95-
return initRuntime(filesDir, cacheDir, testResultsDir, entryPointLibName, args, localDateTimeOffset);
100+
int rv = initRuntime(filesDir, entryPointLibName, localDateTimeOffset);
101+
if (rv != 0) {
102+
Log.e("DOTNET", "Failed to initialize runtime, return-code=" + rv);
103+
freeNativeResources();
104+
System.exit(rv);
105+
}
106+
}
107+
108+
public static int executeEntryPoint(String entryPointLibName, String[] args) {
109+
int rv = execEntryPoint(entryPointLibName, args);
110+
return rv;
96111
}
97112

98113
@Override
@@ -104,7 +119,9 @@ public void onStart() {
104119
finish(1, null);
105120
return;
106121
}
107-
int retcode = initialize(entryPointLibName, argsToForward, getContext());
122+
123+
initializeRuntime(entryPointLibName, getContext());
124+
int retcode = executeEntryPoint(entryPointLibName, argsToForward);
108125

109126
Log.i("DOTNET", "MonoRunner finished, return-code=" + retcode);
110127
result.putInt("return-code", retcode);
@@ -119,6 +136,14 @@ public void onStart() {
119136
finish(retcode, result);
120137
}
121138

139+
@Override
140+
public void onDestroy() {
141+
Log.i("DOTNET", "MonoRunner onDestroy");
142+
super.onDestroy();
143+
// Cleanup native resources
144+
freeNativeResources();
145+
}
146+
122147
static void unzipAssets(Context context, String toPath, String zipName) {
123148
AssetManager assetManager = context.getAssets();
124149
try {
@@ -162,7 +187,11 @@ static int getLocalDateTimeOffset() {
162187
}
163188
}
164189

165-
static native int initRuntime(String libsDir, String cacheDir, String testResultsDir, String entryPointLibName, String[] args, int local_date_time_offset);
166-
167190
static native int setEnv(String key, String value);
191+
192+
static native int initRuntime(String libsDir, String entryPointLibName, int local_date_time_offset);
193+
194+
static native int execEntryPoint(String entryPointLibName, String[] args);
195+
196+
static native void freeNativeResources();
168197
}

Diff for: src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c

+138-56
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,21 @@ void
2424
Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value);
2525

2626
int
27-
Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time);
27+
Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time);
28+
29+
int
30+
Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args);
31+
32+
void
33+
Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz);
2834

2935
/********* implementation *********/
3036

31-
static char *bundle_path;
32-
static char *executable;
37+
static const char* g_bundle_path = NULL;
38+
static const char* g_executable_path = NULL;
39+
static unsigned int g_coreclr_domainId = 0;
40+
static void* g_coreclr_handle = NULL;
41+
3342

3443
#define LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "DOTNET", fmt, ##__VA_ARGS__)
3544
#define LOG_ERROR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "DOTNET", fmt, ##__VA_ARGS__)
@@ -54,21 +63,11 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff)
5463
jboolean isCopy = 0;
5564
const char *copy_buff = (*env)->GetStringUTFChars (env, str, &isCopy);
5665
strncpy (buff, copy_buff, nbuff);
66+
buff[nbuff - 1] = '\0'; // ensure '\0' terminated
5767
if (isCopy)
5868
(*env)->ReleaseStringUTFChars (env, str, copy_buff);
5969
}
6070

61-
void
62-
Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value)
63-
{
64-
LOG_INFO ("Java_net_dot_MonoRunner_setEnv:");
65-
const char *key = (*env)->GetStringUTFChars(env, j_key, 0);
66-
const char *val = (*env)->GetStringUTFChars(env, j_value, 0);
67-
setenv (key, val, true);
68-
(*env)->ReleaseStringUTFChars(env, j_key, key);
69-
(*env)->ReleaseStringUTFChars(env, j_value, val);
70-
}
71-
7271
/*
7372
* Get the list of trusted assemblies from a specified @dir_path.
7473
* The path is searched for .dll files which when found are concatenated
@@ -130,11 +129,67 @@ get_tpas_from_path(const char* dir_path, const char** tpas)
130129
}
131130

132131
static int
133-
mono_droid_runtime_init (const char* executable, int managed_argc, char* managed_argv[], int local_date_time_offset)
132+
bundle_executable_path (const char* executable, const char* bundle_path, const char** executable_path)
134133
{
135-
LOG_INFO ("mono_droid_runtime_init called with executable: %s", executable);
134+
size_t executable_path_len = strlen(bundle_path) + strlen(executable) + 1; // +1 for '/'
135+
char* temp_path = (char*)malloc(sizeof(char) * (executable_path_len + 1)); // +1 for '\0'
136+
if (temp_path == NULL)
137+
{
138+
return -1;
139+
}
136140

137-
chdir (bundle_path);
141+
size_t res = snprintf(temp_path, (executable_path_len + 1), "%s/%s", bundle_path, executable);
142+
if (res < 0 || res != executable_path_len)
143+
{
144+
return -1;
145+
}
146+
*executable_path = temp_path;
147+
return executable_path_len;
148+
}
149+
150+
static void
151+
free_resources ()
152+
{
153+
if (g_bundle_path)
154+
{
155+
free (g_bundle_path);
156+
g_bundle_path = NULL;
157+
}
158+
if (g_executable_path)
159+
{
160+
free (g_executable_path);
161+
g_executable_path = NULL;
162+
}
163+
if (g_coreclr_handle)
164+
{
165+
// Clean up some coreclr resources. This doesn't make coreclr unloadable.
166+
coreclr_shutdown (g_coreclr_handle, g_coreclr_domainId);
167+
g_coreclr_handle = NULL;
168+
}
169+
}
170+
171+
static int
172+
mono_droid_execute_assembly (const char* executable_path, void* coreclr_handle, unsigned int coreclr_domainId, int managed_argc, const char** managed_argv)
173+
{
174+
unsigned int rv;
175+
LOG_INFO ("Calling coreclr_execute_assembly");
176+
coreclr_execute_assembly (coreclr_handle, coreclr_domainId, managed_argc, managed_argv, executable_path, &rv);
177+
LOG_INFO ("Exit code: %u.", rv);
178+
return rv;
179+
}
180+
181+
static int
182+
mono_droid_runtime_init (const char* executable)
183+
{
184+
LOG_INFO ("mono_droid_runtime_init (CoreCLR) called with executable: %s", executable);
185+
186+
if (bundle_executable_path(executable, g_bundle_path, &g_executable_path) < 0)
187+
{
188+
LOG_ERROR("Failed to resolve full path for: %s", executable);
189+
return -1;
190+
}
191+
192+
chdir (g_bundle_path);
138193

139194
// TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES
140195

@@ -145,78 +200,99 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed
145200

146201
const char* appctx_values[3];
147202
appctx_values[0] = ANDROID_RUNTIME_IDENTIFIER;
148-
appctx_values[1] = bundle_path;
149-
size_t tpas_len = get_tpas_from_path(bundle_path, &appctx_values[2]);
203+
appctx_values[1] = g_bundle_path;
204+
size_t tpas_len = get_tpas_from_path(g_bundle_path, &appctx_values[2]);
150205
if (tpas_len < 1)
151206
{
152-
LOG_ERROR("Failed to get trusted assemblies from path: %s", bundle_path);
207+
LOG_ERROR("Failed to get trusted assemblies from path: %s", g_bundle_path);
153208
return -1;
154209
}
155210

156-
size_t executable_path_len = strlen(bundle_path) + strlen(executable) + 2; // +2 for '/' and '\0'
157-
char* executable_path = (char*)malloc(executable_path_len);
158-
size_t res = sprintf (executable_path, "%s/%s", bundle_path, executable);
159-
if (res != executable_path_len - 1)
160-
{
161-
LOG_ERROR("Failed to resolve full path for: %s", executable);
162-
return -1;
163-
}
164-
executable_path[res] = '\0';
165-
166-
unsigned int coreclr_domainId = 0;
167-
void *coreclr_handle = NULL;
168-
169211
LOG_INFO ("Calling coreclr_initialize");
170212
int rv = coreclr_initialize (
171-
executable_path,
213+
g_executable_path,
172214
executable,
173215
3,
174216
appctx_keys,
175217
appctx_values,
176-
&coreclr_handle,
177-
&coreclr_domainId
218+
&g_coreclr_handle,
219+
&g_coreclr_domainId
178220
);
179221
LOG_INFO ("coreclr_initialize returned %d", rv);
222+
return rv;
223+
}
180224

181-
LOG_INFO ("Calling coreclr_execute_assembly");
182-
coreclr_execute_assembly (coreclr_handle, coreclr_domainId, managed_argc, managed_argv, executable_path, &rv);
225+
void
226+
Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value)
227+
{
228+
LOG_INFO ("Java_net_dot_MonoRunner_setEnv:");
229+
assert (g_coreclr_handle == NULL); // setenv should be only called before the runtime is initialized
183230

184-
LOG_INFO ("Exit code: %d.", rv);
185-
return rv;
231+
const char *key = (*env)->GetStringUTFChars(env, j_key, 0);
232+
const char *val = (*env)->GetStringUTFChars(env, j_value, 0);
233+
234+
LOG_INFO ("Setting env: %s=%s", key, val);
235+
setenv (key, val, true);
236+
(*env)->ReleaseStringUTFChars(env, j_key, key);
237+
(*env)->ReleaseStringUTFChars(env, j_value, val);
186238
}
187239

188240
int
189-
Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time)
241+
Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time)
190242
{
191-
LOG_INFO ("Java_net_dot_MonoRunner_initRuntime:");
243+
LOG_INFO ("Java_net_dot_MonoRunner_initRuntime (CoreCLR):");
192244
char file_dir[2048];
193-
char cache_dir[2048];
194-
char testresults_dir[2048];
195245
char entryPointLibName[2048];
196246
strncpy_str (env, file_dir, j_files_dir, sizeof(file_dir));
197-
strncpy_str (env, cache_dir, j_cache_dir, sizeof(cache_dir));
198-
strncpy_str (env, testresults_dir, j_testresults_dir, sizeof(testresults_dir));
199247
strncpy_str (env, entryPointLibName, j_entryPointLibName, sizeof(entryPointLibName));
200248

201-
bundle_path = file_dir;
202-
executable = entryPointLibName;
249+
size_t file_dir_len = strlen(file_dir);
250+
char* bundle_path_tmp = (char*)malloc(sizeof(char) * (file_dir_len + 1)); // +1 for '\0'
251+
if (bundle_path_tmp == NULL)
252+
{
253+
LOG_ERROR("Failed to allocate memory for bundle_path");
254+
return -1;
255+
}
256+
strncpy(bundle_path_tmp, file_dir, file_dir_len + 1);
257+
g_bundle_path = bundle_path_tmp;
258+
259+
return mono_droid_runtime_init (entryPointLibName);
260+
}
261+
262+
int
263+
Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args)
264+
{
265+
LOG_INFO("Java_net_dot_MonoRunner_execEntryPoint (CoreCLR):");
266+
267+
if ((g_bundle_path == NULL) || (g_executable_path == NULL))
268+
{
269+
LOG_ERROR("Bundle path or executable path not set");
270+
return -1;
271+
}
203272

204-
setenv ("HOME", bundle_path, true);
205-
setenv ("TMPDIR", cache_dir, true);
206-
setenv ("TEST_RESULTS_DIR", testresults_dir, true);
273+
if ((g_coreclr_handle == NULL) || (g_coreclr_domainId == 0))
274+
{
275+
LOG_ERROR("CoreCLR not initialized");
276+
return -1;
277+
}
207278

208-
int args_len = (*env)->GetArrayLength(env, j_args);
279+
int args_len = (*env)->GetArrayLength (env, j_args);
209280
int managed_argc = args_len + 1;
210-
char** managed_argv = (char**)malloc(managed_argc * sizeof(char*));
281+
const char** managed_argv = (const char**)malloc (managed_argc * sizeof(char*));
282+
if (managed_argv == NULL)
283+
{
284+
LOG_ERROR("Failed to allocate memory for managed_argv");
285+
return -1;
286+
}
211287

212-
managed_argv[0] = bundle_path;
288+
managed_argv[0] = g_bundle_path;
213289
for (int i = 0; i < args_len; ++i)
214290
{
215291
jstring j_arg = (*env)->GetObjectArrayElement(env, j_args, i);
216292
managed_argv[i + 1] = (char*)((*env)->GetStringUTFChars(env, j_arg, NULL));
217293
}
218294

219-
int res = mono_droid_runtime_init (executable, managed_argc, managed_argv, current_local_time);
295+
int rv = mono_droid_execute_assembly (g_executable_path, g_coreclr_handle, g_coreclr_domainId, managed_argc, managed_argv);
220296

221297
for (int i = 0; i < args_len; ++i)
222298
{
@@ -225,6 +301,12 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_
225301
}
226302

227303
free(managed_argv);
228-
return res;
304+
return rv;
229305
}
230306

307+
void
308+
Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz)
309+
{
310+
LOG_INFO ("Java_net_dot_MonoRunner_freeNativeResources (CoreCLR):");
311+
free_resources ();
312+
}

0 commit comments

Comments
 (0)