Did you know ... | Search Documentation: |
Janus and threads |
Where SWI-Prolog support native preemptively scheduled threads that exploit multiple cores, Python has a single interpreter that can switch between native threads.7Actually, you can create multiple Python interpreters. It is not yet clear to us whether that can help improving on concurrency. Initially the Python interpreter is associated with the thread that created it which, for janus, is the first thread calling Python. The Prolog thread that initiated Janus may terminate. This does not affect the embedded Python interpreter and this interpreter may continue to be used from other Prolog threads.
Janus ensures it holds the Python GIL when interacting with the
Python interpreter. If Python calls Prolog, the GIL is released using
Py_BEGIN_ALLOW_THREADS
.
Prolog may be called safely from any Python thread. The Prolog
execution is embraced with Py_BEGIN_ALLOW_THREADS
and
Py_END_ALLOW_THREADS
, which implies that Python is allowed
to switch to another thread while Prolog is doing its work.
If the calling Python thread is not the one that initiated Janus, janus.query_once() and janus.query() attach and detach a temporary Prolog engine using PL_thread_attach_engine() and PL_thread_destroy_engine(). This is relatively costly. In addition we allow associating a Prolog engine persistently with the calling thread.
NULL
to PL_thread_attach_engine().
Future versions may provide access to the creation attributes.
If the thread already has an engine the attach count is incremented and the current engine id is returned. The engine is detached after a matching number of calls to janus.detach_engine()
In a threaded environment, Python calls must be guarded by PyGILState_Ensure() and PyGILState_Release() that ultimately lock/unlock a mutex. Unfortunately there is no PyGILState_TryEnsure() and therefore we may create deadlocks when Prolog locks are involved. This may either apply to explicit Prolog locks from with_mutex/2 and friends or implicit locks on e.g. I/O streams. The classical scenario is thread A holding the Python GIL and wanting to call Prolog code that locks a mutex M, while thread B holds M and wishes to make a Python call and this tries to lock the GIL. The predicate py_gil_owner/1 can be used to help diagnosing such issues.