[Libguestfs] [PATCH nbdkit 2/4] python: Acquire and release the Python GIL.

Richard W.M. Jones rjones at redhat.com
Wed Aug 5 16:40:07 UTC 2020


With the current thread model this doesn't matter since nbdkit itself
essentially enforces something like the GIL.  But if we want to
parallelize the Python plugin we must actually start using the GIL
right.

This is both complex and very poorly documented (including a lot of
plainly incorrect information online).  However I believe what I have
written here is right, and it appears to work.
---
 plugins/python/python.c | 45 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/plugins/python/python.c b/plugins/python/python.c
index 9fa89964..229a46f1 100644
--- a/plugins/python/python.c
+++ b/plugins/python/python.c
@@ -51,6 +51,19 @@
 
 #include "cleanup.h"
 
+/* All callbacks that want to call any Py* function should use this
+ * macro.  See
+ * https://docs.python.org/3/c-api/init.html#non-python-created-threads
+ */
+#define ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE            \
+  __attribute__((cleanup (cleanup_release)))            \
+  PyGILState_STATE gstate = PyGILState_Ensure()
+static inline void
+cleanup_release (PyGILState_STATE *gstateptr)
+{
+  PyGILState_Release (*gstateptr);
+}
+
 /* XXX Apparently global state is technically wrong in Python 3, see:
  *
  * https://www.python.org/dev/peps/pep-3121/
@@ -304,23 +317,30 @@ create_nbdkit_module (void)
   return m;
 }
 
+static PyThreadState *tstate;
+
 static void
 py_load (void)
 {
   PyImport_AppendInittab ("nbdkit", create_nbdkit_module);
   Py_Initialize ();
+  tstate = PyEval_SaveThread ();
 }
 
 static void
 py_unload (void)
 {
-  Py_XDECREF (module);
-  Py_Finalize ();
+  if (tstate) {
+    PyEval_RestoreThread (tstate);
+    Py_XDECREF (module);
+    Py_Finalize ();
+  }
 }
 
 static void
 py_dump_plugin (void)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   PyObject *fn;
   PyObject *r;
 
@@ -368,6 +388,7 @@ get_py_api_version (void)
 static int
 py_config (const char *key, const char *value)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   int fd;
   FILE *fp;
   PyObject *modname;
@@ -456,6 +477,7 @@ py_config (const char *key, const char *value)
 static int
 py_config_complete (void)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   PyObject *fn;
   PyObject *r;
 
@@ -475,6 +497,7 @@ py_config_complete (void)
 static int
 py_get_ready (void)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   PyObject *fn;
   PyObject *r;
 
@@ -499,6 +522,7 @@ struct handle {
 static void *
 py_open (int readonly)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   PyObject *fn;
   struct handle *h;
 
@@ -531,6 +555,7 @@ py_open (int readonly)
 static void
 py_close (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -551,6 +576,7 @@ py_close (void *handle)
 static int64_t
 py_get_size (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -580,6 +606,7 @@ static int
 py_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
           uint32_t flags)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -642,6 +669,7 @@ static int
 py_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
            uint32_t flags)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -678,6 +706,7 @@ py_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
 static int
 py_flush (void *handle, uint32_t flags)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -710,6 +739,7 @@ py_flush (void *handle, uint32_t flags)
 static int
 py_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -742,6 +772,7 @@ py_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
 static int
 py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -788,6 +819,7 @@ py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
 static int
 py_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -847,36 +879,42 @@ boolean_callback (void *handle, const char *can_fn, const char *plain_fn)
 static int
 py_is_rotational (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   return boolean_callback (handle, "is_rotational", NULL);
 }
 
 static int
 py_can_multi_conn (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   return boolean_callback (handle, "can_multi_conn", NULL);
 }
 
 static int
 py_can_write (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   return boolean_callback (handle, "can_write", "pwrite");
 }
 
 static int
 py_can_flush (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   return boolean_callback (handle, "can_flush", "flush");
 }
 
 static int
 py_can_trim (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   return boolean_callback (handle, "can_trim", "trim");
 }
 
 static int
 py_can_zero (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
 
   if (h->can_zero >= 0)
@@ -887,6 +925,7 @@ py_can_zero (void *handle)
 static int
 py_can_fast_zero (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   int r;
 
   if (callback_defined ("can_fast_zero", NULL))
@@ -904,6 +943,7 @@ py_can_fast_zero (void *handle)
 static int
 py_can_fua (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
@@ -932,6 +972,7 @@ py_can_fua (void *handle)
 static int
 py_can_cache (void *handle)
 {
+  ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
   struct handle *h = handle;
   PyObject *fn;
   PyObject *r;
-- 
2.27.0




More information about the Libguestfs mailing list