Skip to content

Commit 372c2ad

Browse files
committed
Release GIL in Python bindings.
We apply the same changes as: google/jsonnet#814
1 parent 4f527c6 commit 372c2ad

File tree

1 file changed

+89
-15
lines changed

1 file changed

+89
-15
lines changed

python/_jsonnet.c

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ static const char *exc_to_str(void)
4242

4343
struct NativeCtx {
4444
struct JsonnetVm *vm;
45+
PyThreadState **py_thread;
4546
PyObject *callback;
4647
size_t argc;
4748
};
@@ -139,6 +140,8 @@ static struct JsonnetJsonValue *cpython_native_callback(
139140
const struct NativeCtx *ctx = ctx_;
140141
int i;
141142

143+
PyEval_RestoreThread(*ctx->py_thread);
144+
142145
PyObject *arglist; // Will hold a tuple of strings.
143146
PyObject *result; // Will hold a string.
144147

@@ -168,6 +171,7 @@ static struct JsonnetJsonValue *cpython_native_callback(
168171
// TODO(dcunnin): Support objects (to dicts).
169172
Py_DECREF(arglist);
170173
*succ = 0;
174+
*ctx->py_thread = PyEval_SaveThread();
171175
return jsonnet_json_make_string(ctx->vm, "Non-primitive param.");
172176
}
173177
PyTuple_SetItem(arglist, i, pyobj);
@@ -182,6 +186,7 @@ static struct JsonnetJsonValue *cpython_native_callback(
182186
struct JsonnetJsonValue *r = jsonnet_json_make_string(ctx->vm, exc_to_str());
183187
*succ = 0;
184188
PyErr_Clear();
189+
*ctx->py_thread = PyEval_SaveThread();
185190
return r;
186191
}
187192

@@ -193,12 +198,14 @@ static struct JsonnetJsonValue *cpython_native_callback(
193198
*succ = 0;
194199
r = jsonnet_json_make_string(ctx->vm, err_msg);
195200
}
201+
*ctx->py_thread = PyEval_SaveThread();
196202
return r;
197203
}
198204

199205

200206
struct ImportCtx {
201207
struct JsonnetVm *vm;
208+
PyThreadState **py_thread;
202209
PyObject *callback;
203210
};
204211

@@ -209,6 +216,7 @@ static char *cpython_import_callback(void *ctx_, const char *base, const char *r
209216
PyObject *arglist, *result;
210217
char *out;
211218

219+
PyEval_RestoreThread(*ctx->py_thread);
212220
arglist = Py_BuildValue("(s, s)", base, rel);
213221
result = PyEval_CallObject(ctx->callback, arglist);
214222
Py_DECREF(arglist);
@@ -218,6 +226,7 @@ static char *cpython_import_callback(void *ctx_, const char *base, const char *r
218226
char *out = jsonnet_str(ctx->vm, exc_to_str());
219227
*success = 0;
220228
PyErr_Clear();
229+
*ctx->py_thread = PyEval_SaveThread();
221230
return out;
222231
}
223232

@@ -252,6 +261,7 @@ static char *cpython_import_callback(void *ctx_, const char *base, const char *r
252261
}
253262

254263
Py_DECREF(result);
264+
*ctx->py_thread = PyEval_SaveThread();
255265

256266
return out;
257267
}
@@ -340,7 +350,7 @@ int handle_import_callback(struct ImportCtx *ctx, PyObject *import_callback)
340350
* \returns 1 on success, 0 with exception set upon failure.
341351
*/
342352
static int handle_native_callbacks(struct JsonnetVm *vm, PyObject *native_callbacks,
343-
struct NativeCtx **ctxs)
353+
struct NativeCtx **ctxs, PyThreadState **py_thread)
344354
{
345355
size_t num_natives = 0;
346356
PyObject *key, *val;
@@ -433,6 +443,7 @@ static int handle_native_callbacks(struct JsonnetVm *vm, PyObject *native_callba
433443
}
434444
params_c[num_params] = NULL;
435445
(*ctxs)[num_natives].vm = vm;
446+
(*ctxs)[num_natives].py_thread = py_thread;
436447
(*ctxs)[num_natives].callback = PyTuple_GetItem(val, 1);
437448
(*ctxs)[num_natives].argc = num_params;
438449
jsonnet_native_callback(vm, key_, cpython_native_callback, &(*ctxs)[num_natives],
@@ -448,11 +459,12 @@ static int handle_native_callbacks(struct JsonnetVm *vm, PyObject *native_callba
448459
static PyObject* evaluate_file(PyObject* self, PyObject* args, PyObject *keywds)
449460
{
450461
const char *filename;
451-
const char *jpathdir = NULL;
452-
char *out;
462+
char *out, *jpath_str;
453463
unsigned max_stack = 500, gc_min_objects = 1000, max_trace = 20;
454464
double gc_growth_trigger = 2;
455465
int error;
466+
Py_ssize_t num_jpathdir, i;
467+
PyObject *jpathdir = NULL;
456468
PyObject *ext_vars = NULL, *ext_codes = NULL;
457469
PyObject *tla_vars = NULL, *tla_codes = NULL;
458470
PyObject *import_callback = NULL;
@@ -469,47 +481,79 @@ static PyObject* evaluate_file(PyObject* self, PyObject* args, PyObject *keywds)
469481
(void) self;
470482

471483
if (!PyArg_ParseTupleAndKeywords(
472-
args, keywds, "s|sIIdOOOOIOO", kwlist,
484+
args, keywds, "s|OIIdOOOOIOO", kwlist,
473485
&filename, &jpathdir,
474486
&max_stack, &gc_min_objects, &gc_growth_trigger, &ext_vars,
475487
&ext_codes, &tla_vars, &tla_codes, &max_trace, &import_callback,
476488
&native_callbacks)) {
477489
return NULL;
478490
}
479491

492+
PyThreadState *py_thread;
493+
480494
vm = jsonnet_make();
481495
jsonnet_max_stack(vm, max_stack);
482496
jsonnet_gc_min_objects(vm, gc_min_objects);
483497
jsonnet_max_trace(vm, max_trace);
484498
jsonnet_gc_growth_trigger(vm, gc_growth_trigger);
485-
if (jpathdir != NULL)
486-
jsonnet_jpath_add(vm, jpathdir);
499+
500+
if (jpathdir != NULL) {
501+
// Support string for backward compatibility with <= 0.15.0
502+
#if PY_MAJOR_VERSION >= 3
503+
if (PyUnicode_Check(jpathdir)) {
504+
jpath_str = PyUnicode_AsUTF8(jpathdir);
505+
#else
506+
if (PyString_Check(jpathdir)) {
507+
jpath_str = PyString_AsString(jpathdir);
508+
#endif
509+
jsonnet_jpath_add(vm, jpath_str);
510+
} else if (PyList_Check(jpathdir)) {
511+
num_jpathdir = PyList_Size(jpathdir);
512+
for (i = 0; i < num_jpathdir ; ++i) {
513+
PyObject *jpath = PyList_GetItem(jpathdir, i);
514+
#if PY_MAJOR_VERSION >= 3
515+
if (PyUnicode_Check(jpath)) {
516+
jpath_str = PyUnicode_AsUTF8(jpath);
517+
#else
518+
if (PyString_Check(jpath)) {
519+
jpath_str = PyString_AsString(jpath);
520+
#endif
521+
jsonnet_jpath_add(vm, jpath_str);
522+
}
523+
}
524+
}
525+
}
526+
487527
if (!handle_vars(vm, ext_vars, 0, 0)) return NULL;
488528
if (!handle_vars(vm, ext_codes, 1, 0)) return NULL;
489529
if (!handle_vars(vm, tla_vars, 0, 1)) return NULL;
490530
if (!handle_vars(vm, tla_codes, 1, 1)) return NULL;
491-
struct ImportCtx ctx = { vm, import_callback };
531+
532+
struct ImportCtx ctx = { vm, &py_thread, import_callback };
492533
if (!handle_import_callback(&ctx, import_callback)) {
493534
return NULL;
494535
}
495536
struct NativeCtx *ctxs = NULL;
496-
if (!handle_native_callbacks(vm, native_callbacks, &ctxs)) {
537+
if (!handle_native_callbacks(vm, native_callbacks, &ctxs, &py_thread)) {
497538
free(ctxs);
498539
return NULL;
499540
}
541+
py_thread = PyEval_SaveThread();
500542
out = jsonnet_evaluate_file(vm, filename, &error);
543+
PyEval_RestoreThread(py_thread);
501544
free(ctxs);
502545
return handle_result(vm, out, error);
503546
}
504547

505548
static PyObject* evaluate_snippet(PyObject* self, PyObject* args, PyObject *keywds)
506549
{
507550
const char *filename, *src;
508-
const char *jpathdir = NULL;
509-
char *out;
551+
char *out, *jpath_str;
510552
unsigned max_stack = 500, gc_min_objects = 1000, max_trace = 20;
511553
double gc_growth_trigger = 2;
512554
int error;
555+
Py_ssize_t num_jpathdir, i;
556+
PyObject *jpathdir = NULL;
513557
PyObject *ext_vars = NULL, *ext_codes = NULL;
514558
PyObject *tla_vars = NULL, *tla_codes = NULL;
515559
PyObject *import_callback = NULL;
@@ -526,35 +570,65 @@ static PyObject* evaluate_snippet(PyObject* self, PyObject* args, PyObject *keyw
526570
(void) self;
527571

528572
if (!PyArg_ParseTupleAndKeywords(
529-
args, keywds, "ss|sIIdOOOOIOO", kwlist,
573+
args, keywds, "ss|OIIdOOOOIOO", kwlist,
530574
&filename, &src, &jpathdir,
531575
&max_stack, &gc_min_objects, &gc_growth_trigger, &ext_vars,
532576
&ext_codes, &tla_vars, &tla_codes, &max_trace, &import_callback,
533577
&native_callbacks)) {
534578
return NULL;
535579
}
536580

581+
PyThreadState *py_thread;
582+
537583
vm = jsonnet_make();
538584
jsonnet_max_stack(vm, max_stack);
539585
jsonnet_gc_min_objects(vm, gc_min_objects);
540586
jsonnet_max_trace(vm, max_trace);
541587
jsonnet_gc_growth_trigger(vm, gc_growth_trigger);
542-
if (jpathdir != NULL)
543-
jsonnet_jpath_add(vm, jpathdir);
588+
589+
if (jpathdir != NULL) {
590+
// Support string for backward compatibility with <= 0.15.0
591+
#if PY_MAJOR_VERSION >= 3
592+
if (PyUnicode_Check(jpathdir)) {
593+
jpath_str = PyUnicode_AsUTF8(jpathdir);
594+
#else
595+
if (PyString_Check(jpathdir)) {
596+
jpath_str = PyString_AsString(jpathdir);
597+
#endif
598+
jsonnet_jpath_add(vm, jpath_str);
599+
} else if (PyList_Check(jpathdir)) {
600+
num_jpathdir = PyList_Size(jpathdir);
601+
for (i = 0; i < num_jpathdir ; ++i) {
602+
PyObject *jpath = PyList_GetItem(jpathdir, i);
603+
#if PY_MAJOR_VERSION >= 3
604+
if (PyUnicode_Check(jpath)) {
605+
jpath_str = PyUnicode_AsUTF8(jpath);
606+
#else
607+
if (PyString_Check(jpath)) {
608+
jpath_str = PyString_AsString(jpath);
609+
#endif
610+
jsonnet_jpath_add(vm, jpath_str);
611+
}
612+
}
613+
}
614+
}
615+
544616
if (!handle_vars(vm, ext_vars, 0, 0)) return NULL;
545617
if (!handle_vars(vm, ext_codes, 1, 0)) return NULL;
546618
if (!handle_vars(vm, tla_vars, 0, 1)) return NULL;
547619
if (!handle_vars(vm, tla_codes, 1, 1)) return NULL;
548-
struct ImportCtx ctx = { vm, import_callback };
620+
struct ImportCtx ctx = { vm, &py_thread, import_callback };
549621
if (!handle_import_callback(&ctx, import_callback)) {
550622
return NULL;
551623
}
552624
struct NativeCtx *ctxs = NULL;
553-
if (!handle_native_callbacks(vm, native_callbacks, &ctxs)) {
625+
if (!handle_native_callbacks(vm, native_callbacks, &ctxs, &py_thread)) {
554626
free(ctxs);
555627
return NULL;
556628
}
629+
py_thread = PyEval_SaveThread();
557630
out = jsonnet_evaluate_snippet(vm, filename, src, &error);
631+
PyEval_RestoreThread(py_thread);
558632
free(ctxs);
559633
return handle_result(vm, out, error);
560634
}

0 commit comments

Comments
 (0)