Thanks: Nir Soffer.
---
plugins/python/nbdkit-python-plugin.pod | 26 +++++++++++++++-------
plugins/python/python.c | 29 ++++++++++++++++++++++++-
2 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/plugins/python/nbdkit-python-plugin.pod
b/plugins/python/nbdkit-python-plugin.pod
index 852366f0..edd56d13 100644
--- a/plugins/python/nbdkit-python-plugin.pod
+++ b/plugins/python/nbdkit-python-plugin.pod
@@ -163,6 +163,15 @@ There are no arguments or return value.
There are no arguments or return value.
+=item C<thread_model>
+
+(Optional, nbdkit E<ge> 1.22)
+
+ def thread_model():
+ return nbdkit.THEAD_MODEL_SERIALIZE_ALL_REQUESTS
+
+See L</Threads> below.
+
=item C<get_ready>
(Optional)
@@ -367,10 +376,6 @@ optionally using C<nbdkit.set_error> first.
These are not needed because you can just use ordinary Python
constructs.
-=item Missing: C<thread_model>
-
-See L</Threads> below.
-
=item Missing:
C<name>,
C<version>,
@@ -387,10 +392,15 @@ These are not yet supported.
=head2 Threads
-The thread model for Python callbacks currently cannot be set from
-Python. It is hard-coded in the C part to
-C<NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS>. This may change or be
-settable in future.
+The thread model for Python callbacks defaults to
+C<NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS>. Since S<nbdkit 1.22>
+it is possible to set this by implementing a C<thread_model> function
+which returns one of the constants C<nbdkit.THREAD_MODEL_*>.
+
+The Python Global Interpreter Lock (GIL) usually means that you cannot
+execute Python code in parallel, but Python code which calls into
+libraries which block (eg. to make HTTP requests) might be executed in
+parallel.
=head1 FILES
diff --git a/plugins/python/python.c b/plugins/python/python.c
index 229a46f1..398473f5 100644
--- a/plugins/python/python.c
+++ b/plugins/python/python.c
@@ -494,6 +494,28 @@ py_config_complete (void)
return 0;
}
+static int
+py_thread_model (void)
+{
+ ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
+ PyObject *fn;
+ PyObject *r;
+ int ret = NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS;
+
+ if (script && callback_defined ("thread_model", &fn)) {
+ PyErr_Clear ();
+
+ r = PyObject_CallObject (fn, NULL);
+ Py_DECREF (fn);
+ if (check_python_failure ("thread_model") == -1)
+ return -1;
+ ret = PyLong_AsLong (r);
+ Py_DECREF (r);
+ }
+
+ return ret;
+}
+
static int
py_get_ready (void)
{
@@ -1002,7 +1024,11 @@ py_can_cache (void *handle)
"script=<FILENAME> (required) The Python plugin to run.\n" \
"[other arguments may be used by the plugin that you load]"
-#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
+/* This is the maximum possible, but the default for plugins is
+ * NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS. Plugins can override
+ * that by providing a thread_model() function.
+ */
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
static struct nbdkit_plugin plugin = {
.name = "python",
@@ -1016,6 +1042,7 @@ static struct nbdkit_plugin plugin = {
.config_complete = py_config_complete,
.config_help = py_config_help,
+ .thread_model = py_thread_model,
.get_ready = py_get_ready,
.open = py_open,
--
2.27.0