Skip to content

Commit 0ef2199

Browse files
committed
Initialize Python once per process.
Set __main__ variable after acquiring GIL to pass worker and service arguments. Remove ServiceWorker compatibility code now this has been resolved.
1 parent 316565b commit 0ef2199

2 files changed

Lines changed: 52 additions & 22 deletions

File tree

pythonforandroid/bootstraps/common/build/jni/application/src/start.c

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ static int file_exists(const char *filename) {
7777
return 0;
7878
}
7979

80-
static int run_python(int argc, char *argv[], bool call_exit) {
80+
static int gil_init = 0; /* 1 when Python has been initialied/GIL created */
81+
82+
static int run_python(int argc, char *argv[], bool call_exit, char* argument_name, char* argument_value) {
8183

8284
char *env_argument = NULL;
8385
char *env_entrypoint = NULL;
@@ -194,17 +196,26 @@ static int run_python(int argc, char *argv[], bool call_exit) {
194196
" recipes should have this folder, should we expect a crash soon?");
195197
}
196198

197-
Py_Initialize();
198-
LOGP("Initialized python");
199+
if (!gil_init) {
200+
gil_init = 1;
201+
Py_Initialize();
202+
LOGP("Initialized python");
203+
/* ensure threads will work.
204+
*/
205+
LOGP("Calling init threads (unneeded for Python 3.7+)");
206+
PyEval_InitThreads();
207+
PyEval_SaveThread();
208+
} else {
209+
LOGP("Python already initialized in this process");
210+
}
199211

200212
/* Ensure that we are registering this thread against the GIL */
201213
PyGILState_STATE gstate;
214+
LOGP("Attempting to register against the Global Interpreter Lock");
215+
202216
gstate = PyGILState_Ensure();
203217

204-
/* ensure threads will work.
205-
*/
206-
LOGP("AND: Init threads");
207-
PyEval_InitThreads();
218+
LOGP("Registered against the Global Interpreter Lock");
208219

209220
#if PY_MAJOR_VERSION < 3
210221
initandroidembed();
@@ -314,6 +325,30 @@ static int run_python(int argc, char *argv[], bool call_exit) {
314325
return -1;
315326
}
316327

328+
if (argument_name != NULL && argument_value != NULL) {
329+
LOGP("Setting argument name and value in __main__ namespace");
330+
// Calculate the size of the string
331+
int size = snprintf(NULL, 0, "%s = '%s'", argument_name, argument_value);
332+
333+
// Allocate enough memory (plus one for the null terminator)
334+
char* command = malloc(size + 1);
335+
if (command == NULL) {
336+
LOGP("Memory allocation failed\n");
337+
exit(EXIT_FAILURE);
338+
}
339+
340+
// Now format the string
341+
snprintf(command, size + 1, "%s = '%s'", argument_name, argument_value);
342+
343+
// Running the command in the __main__ module context
344+
PyRun_SimpleString(command);
345+
346+
// Free the memory
347+
free(command);
348+
} else {
349+
LOGP("No argument name and value provided");
350+
}
351+
317352
/* run python !
318353
*/
319354
ret = PyRun_SimpleFile(fd, entrypoint);
@@ -347,7 +382,9 @@ static int run_python(int argc, char *argv[], bool call_exit) {
347382
}
348383

349384
/* Release the thread. No Python API allowed beyond this point. */
385+
LOGP("Attempting to release Global Interpreter Lock");
350386
PyGILState_Release(gstate);
387+
LOGP("Released Global Interpreter Lock");
351388

352389
/* This should never actually be reached with call_exit.
353390
*/
@@ -373,6 +410,8 @@ static int native_service_start(
373410
jstring j_python_name,
374411
jstring j_python_home,
375412
jstring j_python_path,
413+
char* argument_name,
414+
char* argument_value,
376415
bool call_exit) {
377416
jboolean iscopy;
378417
const char *android_private =
@@ -402,7 +441,7 @@ static int native_service_start(
402441
/* ANDROID_ARGUMENT points to service subdir,
403442
* so run_python() will run main.py from this dir
404443
*/
405-
return run_python(1, argv, call_exit);
444+
return run_python(1, argv, call_exit, argument_name, argument_value);
406445
}
407446

408447
JNIEXPORT int JNICALL Java_org_kivy_android_PythonService_nativeStart(
@@ -427,6 +466,8 @@ JNIEXPORT int JNICALL Java_org_kivy_android_PythonService_nativeStart(
427466
j_python_name,
428467
j_python_home,
429468
j_python_path,
469+
"PYTHON_SERVICE_ARGUMENT",
470+
arg,
430471
true);
431472
}
432473

@@ -452,6 +493,8 @@ JNIEXPORT int JNICALL Java_org_kivy_android_PythonWorker_nativeStart(
452493
j_python_name,
453494
j_python_home,
454495
j_python_path,
496+
"PYTHON_WORKER_ARGUMENT",
497+
arg,
455498
false);
456499
}
457500

@@ -495,7 +538,7 @@ int Java_org_kivy_android_PythonActivity_nativeInit(JNIEnv* env, jclass cls, job
495538
argv[1] = NULL;
496539
/* status = SDL_main(1, argv); */
497540

498-
return run_python(1, argv, true);
541+
return run_python(1, argv, true, NULL, NULL);
499542

500543
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
501544
/* exit(status); */

pythonforandroid/bootstraps/common/build/templates/WorkerService.tmpl.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,4 @@ public void onCreate() {
2525
}
2626
super.onCreate();
2727
}
28-
29-
@Override
30-
public void onDestroy() {
31-
super.onDestroy();
32-
33-
// The process needs to exit when the service is destroyed since
34-
// p4a doesn't support starting a Python interpreter more than
35-
// once per process. Combined with the stopWithTask="true"
36-
// configuration in the manifest, this should ensure that the
37-
// service process exits when a task completes.
38-
Log.v(TAG, "Exiting service process");
39-
System.exit(0);
40-
}
4128
}

0 commit comments

Comments
 (0)