diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -232,6 +232,14 @@ returned as a string. The default is @code{False}, in which case the return value is @code{None}. If @var{to_string} is @code{True}, the @value{GDBN} virtual terminal will be temporarily set to unlimited width and height, and its pagination will be disabled; @pxref{Screen Size}. + +The @var{release_gil} flag specifies whether @value{GDBN} ought to +release the Python GIL before executing the command. This is useful +in multi-threaded Python programs where by default the Python +interpreter will acquire the GIL and lock other threads from +executing. After the command has completed executing in @value{GDBN} +the Python GIL is reacquired. This flag must be a boolean value. If +omitted, it defaults to @code{False}. @end defun @findex gdb.breakpoints diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -148,6 +148,8 @@ typedef int Py_ssize_t; #define PyGILState_Release(ARG) ((void)(ARG)) #define PyEval_InitThreads() #define PyThreadState_Swap(ARG) ((void)(ARG)) +#define PyEval_SaveThread() ((void)(ARG)) +#define PyEval_RestoreThread(ARG) ((void)(ARG)) #define PyEval_ReleaseLock() #endif diff --git a/gdb/python/python.c b/gdb/python/python.c --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -556,12 +556,16 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) { const char *arg; PyObject *from_tty_obj = NULL, *to_string_obj = NULL; - int from_tty, to_string; - static const char *keywords[] = { "command", "from_tty", "to_string", NULL }; + int from_tty, to_string, release_gil; + static const char *keywords[] = {"command", "from_tty", "to_string", "release_gil", NULL }; + PyObject *release_gil_obj = NULL; + /* Initialize it just to avoid a GCC false warning. */ + PyThreadState *state = NULL; - if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg, + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!O!", keywords, &arg, &PyBool_Type, &from_tty_obj, - &PyBool_Type, &to_string_obj)) + &PyBool_Type, &to_string_obj, + &PyBool_Type, &release_gil_obj)) return NULL; from_tty = 0; @@ -582,6 +586,15 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) to_string = cmp; } + release_gil = 0; + if (release_gil_obj) + { + int cmp = PyObject_IsTrue (release_gil_obj); + if (cmp < 0) + return NULL; + release_gil = cmp; + } + std::string to_string_res; TRY @@ -602,6 +615,13 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) counted_command_line lines = read_command_lines_1 (reader, 1, nullptr); + /* In the case of long running GDB commands, allow the user to + release the Python GIL acquired by Python. Restore the GIL + after the command has completed before handing back to + Python. */ + if (release_gil) + state = PyEval_SaveThread(); + scoped_restore save_async = make_scoped_restore (¤t_ui->async, 0); scoped_restore save_uiout = make_scoped_restore (¤t_uiout); @@ -617,10 +637,22 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) from_tty); else execute_control_commands (lines.get (), from_tty); + + /* Reacquire the GIL if it was released earlier. */ + if (release_gil) + PyEval_RestoreThread (state); } CATCH (except, RETURN_MASK_ALL) { - GDB_PY_HANDLE_EXCEPTION (except); + if (except.reason < 0) + { + /* Reacquire the GIL if it was released earlier. */ + if (release_gil) + PyEval_RestoreThread (state); + + gdbpy_convert_exception (except); + return NULL; + } } END_CATCH