Skip to content

Commit 3ddc1bb

Browse files
committed
ADDED: py_with_gil/1 and use the GIL in pl_call/1,2
Does deadlock. Seems this could be related to python/cpython#96071
1 parent 26d4e95 commit 3ddc1bb

File tree

3 files changed

+43
-8
lines changed

3 files changed

+43
-8
lines changed

janus.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -674,32 +674,39 @@ py_call(term_t Call, term_t result)
674674
{ PyObject *py_target = NULL;
675675
term_t call = PL_copy_term_ref(Call);
676676
term_t on = PL_new_term_ref();
677+
int rc = TRUE;
677678

678679
if ( !py_init() )
679680
return FALSE;
680681

682+
PyGILState_STATE state = PyGILState_Ensure();
681683
while ( PL_is_functor(call, FUNCTOR_module2) )
682684
{ _PL_get_arg(1, call, on);
683685
_PL_get_arg(2, call, call);
684686

685687
if ( !py_target )
686688
{ if ( !get_py_obj(on, &py_target, FALSE) &&
687689
!get_py_module(on, &py_target) )
688-
return PL_type_error("py_target", on);
690+
{ rc = PL_type_error("py_target", on);
691+
break;
692+
}
689693
Py_INCREF(py_target);
690694
} else
691695
{ if ( !(py_target = py_eval(py_target, on)) )
692-
return FALSE;
696+
{ rc = FALSE;
697+
break;
698+
}
693699
}
694700
}
695701

696-
if ( !(py_target = py_eval(py_target, call)) )
697-
return FALSE;
702+
rc = rc && !!(py_target = py_eval(py_target, call));
698703

699-
if ( result )
700-
return py_unify_decref(result, py_target);
701-
else
702-
return TRUE;
704+
if ( rc && result )
705+
rc = py_unify_decref(result, py_target);
706+
707+
PyGILState_Release(state);
708+
709+
return rc;
703710
}
704711

705712
static foreign_t
@@ -708,6 +715,16 @@ py_call1(term_t Call)
708715
}
709716

710717

718+
static foreign_t
719+
py_with_gil(term_t goal)
720+
{ PyGILState_STATE state = PyGILState_Ensure();
721+
int rc = PL_call(goal, NULL);
722+
PyGILState_Release(state);
723+
724+
return rc;
725+
}
726+
727+
711728
static foreign_t
712729
py_str(term_t t, term_t str)
713730
{ PyObject *obj;
@@ -750,6 +767,7 @@ install_janus(void)
750767
PL_register_foreign("py_initialize", 2, py_initialize, 0);
751768
PL_register_foreign("py_call", 2, py_call, 0);
752769
PL_register_foreign("py_call", 1, py_call1, 0);
770+
PL_register_foreign("py_with_gil", 1, py_with_gil, PL_FA_TRANSPARENT);
753771
PL_register_foreign("py_str", 2, py_str, 0);
754772

755773
if ( PyImport_AppendInittab("swipl", PyInit_swipl) == -1 )

janus.pl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
:- module(janus,
3636
[ py_call/1, % +Call
3737
py_call/2, % +Call, -Return
38+
py_with_gil/1, % :Goal
3839
py_str/2, % +Obj, -String
3940
py_initialize/2, % +Program, +Argv
4041
py_lib_dirs/1, % -Dirs
@@ -43,6 +44,7 @@
4344
py_shell/0
4445
]).
4546
:- use_foreign_library(foreign(janus)).
47+
:- meta_predicate py_with_gil(0).
4648

4749
:- public
4850
py_initialize/0,
@@ -111,6 +113,14 @@
111113
% ?- py_call($Dog:tricks, Tricks).
112114
% Tricks = ["roll_over"]
113115

116+
%! py_with_gil(:Goal) is semidet.
117+
%
118+
% Run Goal as once(Goal) while holding the Phyton GIL (_Global
119+
% Interpreter Lock). Note that py_call/1,2 also locks the GIL. This
120+
% predicate is only required if we wish to make multiple calls to
121+
% Python while keeping the GIL. The GIL is a _recursive_ lock and thus
122+
% calling py_call/1,2 while hilding the GIL does not _deadlock_.
123+
114124

115125
/*******************************
116126
* INIT *

tests/prolog.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,10 @@ def bench_call(n):
2222

2323
def echo(d):
2424
return d
25+
26+
27+
class Counter:
28+
def __init__(self):
29+
self.count = 0
30+
def increment(self):
31+
self.count += 1

0 commit comments

Comments
 (0)