Richard W.M. Jones
2020-Aug-05 15:40 UTC
[Libguestfs] [PATCH NOT WORKING nbdkit 0/3] python: Allow thread model to be set from Python plugins.
Patch 2 certainly allows you to set the thread model. However patch 3
shows that if you set it to nbdkit.THREAD_MODEL_PARALLEL it will
crash.
If you look closely at the stack trace (attached below) you can see
that ignoring threads which are in parts of nbdkit unrelated to
Python:
Thread 4: In pread, waiting in time.sleep(). This thread has released
the GIL.
Thread 2: Started to process a pread call but didn't reach Python code yet.
Thread 1: In pread, segfaults when checking if pread() is defined
in the Python code.
My understanding is this should all be OK and there's no reason for
Python to crash here. I wonder if it's because we're calling
"down"
into Python from C, rather than the usual way of calling from Python
into C.
Rich.
Core was generated by `/home/rjones/d/nbdkit/server/nbdkit -v -P
test-python-thread-model.pid -U /tmp/'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007fc4b943e97c in find_name_in_mro (type=<optimized out>,
name=0x7fc4aba406b0, error=0x7fc4aa21e7d4)
at /usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Python/errors.c:221
221 PyErr_Occurred(void)
[Current thread is 1 (Thread 0x7fc4aa21f640 (LWP 109870))]
glibc-2.31.9000-21.fc33.x86_64 gmp-6.1.2-12.fc32.x86_64
libidn2-2.3.0-1.fc32.x86_64 libselinux-3.1-1.fc33.x86_64
libtasn1-4.15.0-1.fc32.x86_64 p11-kit-0.23.18.1-1.fc32.x86_64
pcre2-10.34-4.fc32.x86_64
(gdb) t a a bt
Thread 9 (Thread 0x7fc4a9a1e640 (LWP 109871)):
#0 0x00007fc4b9d32e5b in __lll_lock_wait_private () from /lib64/libpthread.so.0
#1 0x00007fc4b9d34115 in flockfile () from /lib64/libpthread.so.0
#2 0x000000000040a053 in nbdkit_debug (fs=0x41bada "starting worker thread
%s") at debug.c:91
#3 0x00000000004081b7 in connection_worker (data=0x9ee9ff0) at
connections.c:116
#4 0x00007fc4b9d293f9 in start_thread () from /lib64/libpthread.so.0
#5 0x00007fc4b9c55b23 in clone () from /lib64/libc.so.6
Thread 8 (Thread 0x7fc4aaa20640 (LWP 109869)):
#0 0x00007fc4b9d32e5b in __lll_lock_wait_private () from /lib64/libpthread.so.0
#1 0x00007fc4b9d34115 in flockfile () from /lib64/libpthread.so.0
#2 0x000000000040a053 in nbdkit_debug (fs=0x41b3ea "%s: pread count=%u
offset=%lu") at debug.c:91
#3 0x0000000000406507 in backend_pread (b=0x9f07040, buf=0x9f80dc0, count=512,
offset=0, flags=0, err=0x7fc4aaa1fa78) at backend.c:482
#4 0x0000000000411f7e in handle_request (cmd=0, flags=0, offset=0, count=512,
buf=0x9f80dc0, extents=0x0) at protocol.c:241
#5 0x00000000004131c9 in protocol_recv_request_send_reply () at protocol.c:713
#6 0x00000000004081e7 in connection_worker (data=0x9ee9ff0) at
connections.c:123
#7 0x00007fc4b9d293f9 in start_thread () from /lib64/libpthread.so.0
#8 0x00007fc4b9c55b23 in clone () from /lib64/libc.so.6
Thread 7 (Thread 0x7fc4aba22640 (LWP 109867)):
#0 0x00007fc4b9c5079b in mprotect () from /lib64/libc.so.6
#1 0x00007fc4b9d2a0e1 in pthread_create@@GLIBC_2.2.5 () from
/lib64/libpthread.so.0
#2 0x0000000000408491 in handle_single_connection (sockin=8, sockout=8) at
connections.c:204
#3 0x0000000000418824 in start_thread (datav=0x9ee9b10) at sockets.c:337
#4 0x00007fc4b9d293f9 in start_thread () from /lib64/libpthread.so.0
#5 0x00007fc4b9c55b23 in clone () from /lib64/libc.so.6
Thread 6 (Thread 0x7fc4b96bb200 (LWP 109858)):
#0 0x00007fc4b9c4aa2f in poll () from /lib64/libc.so.6
#1 0x0000000000418af3 in check_sockets_and_quit_fd (socks=0x7ffe7d9b3b80) at
sockets.c:447
#2 0x0000000000418bd7 in accept_incoming_connections (socks=0x7ffe7d9b3b80) at
sockets.c:475
#3 0x000000000040f6c9 in start_serving () at main.c:974
#4 0x000000000040ef8b in main (argc=9, argv=0x7ffe7d9b3de8) at main.c:736
Thread 5 (Thread 0x7fc4a921d640 (LWP 109872)):
#0 0x00007fc4b9c464ef in write () from /lib64/libc.so.6
#1 0x00007fc4b9bd5e5d in _IO_file_write@@GLIBC_2.2.5 () from /lib64/libc.so.6
#2 0x00007fc4b9bd5196 in new_do_write () from /lib64/libc.so.6
#3 0x00007fc4b9bd6f49 in __GI__IO_do_write () from /lib64/libc.so.6
#4 0x00007fc4b9bd73b3 in __GI__IO_file_overflow () from /lib64/libc.so.6
#5 0x00007fc4b9bd2423 in fputc () from /lib64/libc.so.6
#6 0x000000000040a0b9 in nbdkit_debug (fs=0x41bada "starting worker thread
%s") at debug.c:98
#7 0x00000000004081b7 in connection_worker (data=0x9ee9cb0) at
connections.c:116
#8 0x00007fc4b9d293f9 in start_thread () from /lib64/libpthread.so.0
#9 0x00007fc4b9c55b23 in clone () from /lib64/libc.so.6
Thread 4 (Thread 0x7fc4ab221640 (LWP 109868)):
#0 0x00007fc4b9c4d1bb in select () from /lib64/libc.so.6
#1 0x00007fc4b95321e9 in pysleep (secs=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Modules/timemodule.c:1909
#2 time_sleep (self=<optimized out>, obj=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Modules/timemodule.c:341
#3 0x00007fc4b9451a62 in cfunction_vectorcall_O (func=<built-in method sleep
of module object at remote 0x7fc4abc41540>, args=0x7fc4abc3ffd0,
nargsf=<optimized out>, kwnames=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/methodobject.c:510
#4 0x00007fc4b944a0a7 in _PyObject_VectorcallTstate (kwnames=0x0,
nargsf=<optimized out>, args=0x7fc4abc3ffd0, callable=<built-in method
sleep of module object at remote 0x7fc4abc41540>, tstate=0x9f0a610) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Include/cpython/abstract.h:118
#5 PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>,
args=0x7fc4abc3ffd0, callable=<built-in method sleep of module object at
remote 0x7fc4abc41540>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Include/cpython/abstract.h:127
#6 call_function (kwnames=0x0, oparg=<optimized out>,
pp_stack=<synthetic pointer>, tstate=0x9f0a610) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Python/ceval.c:5044
#7 _PyEval_EvalFrameDefault (tstate=<optimized out>, f=<optimized
out>, throwflag=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Python/ceval.c:3459
#8 0x00007fc4b945270b in _PyEval_EvalFrame (throwflag=0, f=Frame
0x7fc4abc3fe40, for file ./python-thread-model.py, line 49, in pread (h={},
count=512, offset=0), tstate=0x9f0a610) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Include/internal/pycore_ceval.h:40
#9 function_code_fastcall (tstate=0x9f0a610, co=<optimized out>,
args=<optimized out>, nargs=3, globals=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/call.c:329
#10 0x00007fc4b9455276 in _PyObject_VectorcallTstate (kwnames=0x0,
nargsf=<optimized out>, args=0x7fc4ab220790, callable=<function at
remote 0x7fc4aba34a60>, tstate=0x9f0a610) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Include/cpython/abstract.h:81
#11 _PyObject_CallFunctionVa (tstate=0x9f0a610, callable=<function at remote
0x7fc4aba34a60>, format=<optimized out>, va=<optimized out>,
is_size_t=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/call.c:542
#12 0x00007fc4b94c8762 in _PyObject_CallFunction_SizeT (callable=<optimized
out>, format=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/call.c:596
#13 0x00007fc4b9f7b840 in py_pread (handle=0x9ee9f20, buf=0x9f98ca0, count=512,
offset=0, flags=0) at python.c:619
#14 0x0000000000410d89 in plugin_pread (b=0x9f07040, handle=0x9ee9f20,
buf=0x9f98ca0, count=512, offset=0, flags=0, err=0x7fc4ab220a78) at
plugins.c:524
#15 0x000000000040653f in backend_pread (b=0x9f07040, buf=0x9f98ca0, count=512,
offset=0, flags=0, err=0x7fc4ab220a78) at backend.c:485
#16 0x0000000000411f7e in handle_request (cmd=0, flags=0, offset=0, count=512,
buf=0x9f98ca0, extents=0x0) at protocol.c:241
#17 0x00000000004131c9 in protocol_recv_request_send_reply () at protocol.c:713
#18 0x00000000004081e7 in connection_worker (data=0x9ee9cb0) at
connections.c:123
#19 0x00007fc4b9d293f9 in start_thread () from /lib64/libpthread.so.0
#20 0x00007fc4b9c55b23 in clone () from /lib64/libc.so.6
Thread 3 (Thread 0x7fc4a821b640 (LWP 109874)):
#0 0x00007fc4b9c55b15 in clone () from /lib64/libc.so.6
#1 0x00007fc4b9d29310 in annobin_start_thread.start () from
/lib64/libpthread.so.0
#2 0x00007fc4a821b640 in ?? ()
#3 0x0000000000000000 in ?? ()
Thread 2 (Thread 0x7fc4a8a1c640 (LWP 109873)):
#0 0x00007fc4b9d32e5b in __lll_lock_wait_private () from /lib64/libpthread.so.0
#1 0x00007fc4b9d34115 in flockfile () from /lib64/libpthread.so.0
#2 0x000000000040a053 in nbdkit_debug (fs=0x41b3ea "%s: pread count=%u
offset=%lu") at debug.c:91
#3 0x0000000000406507 in backend_pread (b=0x9f07040, buf=0x9f7cc40, count=512,
offset=0, flags=0, err=0x7fc4a8a1ba78) at backend.c:482
#4 0x0000000000411f7e in handle_request (cmd=0, flags=0, offset=0, count=512,
buf=0x9f7cc40, extents=0x0) at protocol.c:241
#5 0x00000000004131c9 in protocol_recv_request_send_reply () at protocol.c:713
#6 0x00000000004081e7 in connection_worker (data=0x9eea670) at
connections.c:123
#7 0x00007fc4b9d293f9 in start_thread () from /lib64/libpthread.so.0
#8 0x00007fc4b9c55b23 in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7fc4aa21f640 (LWP 109870)):
#0 0x00007fc4b943e97c in find_name_in_mro (type=<optimized out>,
name='pread', error=0x7fc4aa21e7d4) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Python/errors.c:221
#1 0x00007fc4b943e7e3 in _PyType_Lookup (type=0x7fc4b966c760
<PyModule_Type>, name='pread') at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/typeobject.c:3232
#2 0x00007fc4b94512bd in _PyObject_GenericGetAttrWithDict (obj=<module at
remote 0x7fc4abbfabd0>, name='pread', dict=0x0, suppress=0) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/object.c:1194
#3 0x00007fc4b9455194 in PyObject_GenericGetAttr (name='pread',
obj=<module at remote 0x7fc4abbfabd0>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/object.c:1278
#4 module_getattro (m=0x7fc4abbfabd0, name='pread') at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/moduleobject.c:717
#5 0x00007fc4b94c2014 in PyObject_GetAttrString (v=<module at remote
0x7fc4abbfabd0>, name=<optimized out>) at
/usr/src/debug/python3.9-3.9.0~b3-1.fc33.x86_64/Objects/object.c:795
#6 0x00007fc4b9f7a5e8 in callback_defined (name=0x7fc4b9f7d4b6
"pread", obj_rtn=0x7fc4aa21e940) at python.c:77
#7 0x00007fc4b9f7b7c8 in py_pread (handle=0x9ee9f20, buf=0x9f88890, count=512,
offset=0, flags=0) at python.c:610
#8 0x0000000000410d89 in plugin_pread (b=0x9f07040, handle=0x9ee9f20,
buf=0x9f88890, count=512, offset=0, flags=0, err=0x7fc4aa21ea78) at
plugins.c:524
#9 0x000000000040653f in backend_pread (b=0x9f07040, buf=0x9f88890, count=512,
offset=0, flags=0, err=0x7fc4aa21ea78) at backend.c:485
#10 0x0000000000411f7e in handle_request (cmd=0, flags=0, offset=0, count=512,
buf=0x9f88890, extents=0x0) at protocol.c:241
#11 0x00000000004131c9 in protocol_recv_request_send_reply () at protocol.c:713
#12 0x00000000004081e7 in connection_worker (data=0x9ee9cb0) at
connections.c:123
#13 0x00007fc4b9d293f9 in start_thread () from /lib64/libpthread.so.0
#14 0x00007fc4b9c55b23 in clone () from /lib64/libc.so.6
Richard W.M. Jones
2020-Aug-05 15:40 UTC
[Libguestfs] [PATCH nbdkit 1/3] python: Make last_error into a thread-local variable.
While with the current thread model this makes no difference, it is required if we want to allow more parallel thread models. --- plugins/python/python.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/python/python.c b/plugins/python/python.c index f21a1602..9fa89964 100644 --- a/plugins/python/python.c +++ b/plugins/python/python.c @@ -63,7 +63,7 @@ static const char *script; static PyObject *module; static int py_api_version = 1; -static int last_error; +static __thread int last_error; /* Is a callback defined? */ static int -- 2.27.0
Richard W.M. Jones
2020-Aug-05 15:40 UTC
[Libguestfs] [PATCH nbdkit 2/3] python: Allow thread model to be set from Python code.
Thanks: Nir Soffer.
---
plugins/python/nbdkit-python-plugin.pod | 26 ++++++++++++++++-------
plugins/python/python.c | 28 ++++++++++++++++++++++++-
2 files changed, 45 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 9fa89964..aa19475b 100644
--- a/plugins/python/python.c
+++ b/plugins/python/python.c
@@ -472,6 +472,27 @@ py_config_complete (void)
return 0;
}
+static int
+py_thread_model (void)
+{
+ PyObject *fn;
+ PyObject *r;
+ int ret = NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS;
+
+ if (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)
{
@@ -961,7 +982,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",
@@ -975,6 +1000,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
Richard W.M. Jones
2020-Aug-05 15:40 UTC
[Libguestfs] [PATCH nbdkit 3/3] python: Test the parallel thread model.
---
tests/Makefile.am | 3 ++
tests/test-python-thread-model.sh | 84 +++++++++++++++++++++++++++++++
tests/python-thread-model.py | 50 ++++++++++++++++++
3 files changed, 137 insertions(+)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 79be5639..2aa9e14c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1020,15 +1020,18 @@ TESTS += \
test-python.sh \
test-python-exception.sh \
test-python-export-name.sh \
+ test-python-thread-model.sh \
test-shebang-python.sh \
$(NULL)
EXTRA_DIST += \
python-exception.py \
python-export-name.py \
+ python-thread-model.py \
shebang.py \
test-python-exception.sh \
test-python-export-name.sh \
test-python-plugin.py \
+ test-python-thread-model.sh \
test-python.sh \
test-shebang-python.sh \
test_python.py \
diff --git a/tests/test-python-thread-model.sh
b/tests/test-python-thread-model.sh
new file mode 100755
index 00000000..35bb5160
--- /dev/null
+++ b/tests/test-python-thread-model.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2018-2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS
IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+source ./functions.sh
+set -e
+set -x
+
+SCRIPT="$SRCDIR/python-thread-model.py"
+if ! test -d "$SRCDIR" || ! test -f "$SCRIPT"; then
+ echo "$0: could not locate python-thread-model.py"
+ exit 1
+fi
+
+# Python has proven very difficult to valgrind, therefore it is disabled.
+if [ "$NBDKIT_VALGRIND" = "1" ]; then
+ echo "$0: skipping Python test under valgrind."
+ exit 77
+fi
+
+requires nbdsh --version
+
+out=test-python-thread-model.out
+pid=test-python-thread-model.pid
+sock=`mktemp -u`
+files="$out $pid $sock"
+rm -f $files
+cleanup_fn rm -f $files
+
+# Check the plugin is loadable and the effective thread model is parallel.
+nbdkit python $SCRIPT --dump-plugin >$out
+grep "^thread_model=parallel" $out
+
+start_nbdkit -P $pid -U $sock python $SCRIPT
+
+export sock
+nbdsh -c '
+import os
+import time
+
+h.connect_unix (os.environ["sock"])
+
+# We should be able to issue multiple requests in parallel,
+# and the total time taken should not be much more than 10 seconds
+# because all sleeps in the plugin should happen in parallel.
+start_t = time.time()
+for i in range (10):
+ buf = nbd.Buffer (512)
+ h.aio_pread (buf, 0)
+
+while h.aio_in_flight() > 0:
+ h.poll(-1)
+end_t = time.time()
+
+print (end_t - start_t)
+'
diff --git a/tests/python-thread-model.py b/tests/python-thread-model.py
new file mode 100644
index 00000000..67879e04
--- /dev/null
+++ b/tests/python-thread-model.py
@@ -0,0 +1,50 @@
+# nbdkit
+# Copyright (C) 2018-2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS
IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Python plugin which uses the parallel thread model. Note that the
+# cpython implementation of time.sleep releases the GIL (see
+# Modules/timemodule.c:pysleep)
+
+import nbdkit
+import time
+
+def thread_model():
+ return nbdkit.THREAD_MODEL_PARALLEL
+
+def open(readonly):
+ return {}
+
+def get_size(h):
+ return 512
+
+def pread(h, count, offset):
+ time.sleep(10)
+ return bytearray(count)
--
2.27.0
Richard W.M. Jones
2020-Aug-05 15:46 UTC
Re: [Libguestfs] [PATCH NOT WORKING nbdkit 0/3] python: Allow thread model to be set from Python plugins.
Nevermind, I discovered: https://docs.python.org/3/c-api/init.html Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
Daniel P. Berrangé
2020-Aug-05 15:50 UTC
Re: [Libguestfs] [PATCH NOT WORKING nbdkit 0/3] python: Allow thread model to be set from Python plugins.
On Wed, Aug 05, 2020 at 04:40:26PM +0100, Richard W.M. Jones wrote:> Patch 2 certainly allows you to set the thread model. However patch 3 > shows that if you set it to nbdkit.THREAD_MODEL_PARALLEL it will > crash. > > If you look closely at the stack trace (attached below) you can see > that ignoring threads which are in parts of nbdkit unrelated to > Python: > > Thread 4: In pread, waiting in time.sleep(). This thread has released > the GIL.Has it - I'm not seeing any call to PyEval_SaveThread()/RestoreThread which is needed to release the GIL when going from Python to C.> > Thread 2: Started to process a pread call but didn't reach Python code yet. > > Thread 1: In pread, segfaults when checking if pread() is defined > in the Python code. > > My understanding is this should all be OK and there's no reason for > Python to crash here. I wonder if it's because we're calling "down" > into Python from C, rather than the usual way of calling from Python > into C.I'm also not seeing any coodes to PyGILState_Ensure()/Release which is needed call into Python from C. Without these calls, random crashes are certainly expected. What libvirt-python does is eg PyThreadState *_save = NULL; if (PyEval_ThreadsInitialized()) _save = PyEval_SaveThread(); ...call C functions... if (PyEval_ThreadsInitialized()) PyEval_RestoreThread(_save); and PyGILState_STATE _save = PyGILState_UNLOCKED; if (PyEval_ThreadsInitialized()) _save = PyGILState_Ensure(); ... call Python functions... if (PyEval_ThreadsInitialized()) PyGILState_Release(_save); Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|