Index: gdb-7.99.90.20170420/gdb/doc/python.texi =================================================================== --- gdb-7.99.90.20170420.orig/gdb/doc/python.texi 2017-04-20 23:16:30.895328018 +0200 +++ gdb-7.99.90.20170420/gdb/doc/python.texi 2017-04-20 23:16:35.690360136 +0200 @@ -230,6 +230,14 @@ 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 Index: gdb-7.99.90.20170420/gdb/python/python-internal.h =================================================================== --- gdb-7.99.90.20170420.orig/gdb/python/python-internal.h 2017-04-20 23:16:30.896328024 +0200 +++ gdb-7.99.90.20170420/gdb/python/python-internal.h 2017-04-20 23:16:35.690360136 +0200 @@ -142,6 +142,8 @@ #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 Index: gdb-7.99.90.20170420/gdb/python/python.c =================================================================== --- gdb-7.99.90.20170420.orig/gdb/python/python.c 2017-04-20 23:16:30.897328031 +0200 +++ gdb-7.99.90.20170420/gdb/python/python.c 2017-04-20 23:18:11.377001070 +0200 @@ -594,12 +594,16 @@ { 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; @@ -620,6 +624,15 @@ 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 @@ -628,6 +641,13 @@ std::string copy (arg); struct interp *interp; + /* 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); @@ -642,10 +662,22 @@ to_string_res = execute_command_to_string (©[0], from_tty); else execute_command (©[0], 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 Index: gdb-7.99.90.20170420/gdb/testsuite/gdb.python/py-gil-mthread.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.99.90.20170420/gdb/testsuite/gdb.python/py-gil-mthread.c 2017-04-20 23:16:35.691360143 +0200 @@ -0,0 +1,13 @@ +#include +#include + +int +main (void) +{ + int i; + for (i = 0; i < 10; i++) + { + sleep (1); /* break-here */ + printf ("Sleeping %d\n", i); + } +} Index: gdb-7.99.90.20170420/gdb/testsuite/gdb.python/py-gil-mthread.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.99.90.20170420/gdb/testsuite/gdb.python/py-gil-mthread.exp 2017-04-20 23:16:35.691360143 +0200 @@ -0,0 +1,69 @@ +# Copyright (C) 2014 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +standard_testfile .c .py +set executable $testfile + +if { [prepare_for_testing $testfile.exp $executable $srcfile] } { + return -1 +} + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +if ![runto_main] { + return -1 +} + +gdb_breakpoint $srcfile:[gdb_get_line_number "break-here"] temporary +gdb_continue_to_breakpoint "break-here" ".* break-here .*" + +set test "response" +set timeout 60 +set sleeping_last -1 +set hello_last 0 +set minimal 5 +gdb_test_multiple "python exec (open ('$srcdir/$subdir/$srcfile2').read ())" $test { + -re "Error: unable to start thread\r\n" { + fail $test + # Not $gdb_prompt-synced! + } + -re "Sleeping (\[0-9\]+)\r\n" { + set n $expect_out(1,string) + if { $sleeping_last + 1 != $n } { + fail $test + } else { + set sleeping_last $n + if { $sleeping_last >= $minimal && $hello_last >= $minimal } { + pass $test + } else { + exp_continue + } + } + } + -re "Hello \\( (\[0-9\]+) \\)\r\n" { + set n $expect_out(1,string) + if { $hello_last + 1 != $n } { + fail $test + } else { + set hello_last $n + if { $sleeping_last >= $minimal && $hello_last >= $minimal } { + pass $test + } else { + exp_continue + } + } + } +} Index: gdb-7.99.90.20170420/gdb/testsuite/gdb.python/py-gil-mthread.py =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.99.90.20170420/gdb/testsuite/gdb.python/py-gil-mthread.py 2017-04-20 23:16:35.692360149 +0200 @@ -0,0 +1,28 @@ +try: + import thread +except: + import _thread +import time +import gdb + +# Define a function for the thread +def print_thread_hello(): + count = 0 + while count < 10: + time.sleep(1) + count += 1 + print ("Hello ( %d )" % count) + +# Create a threads a continue +try: + thread.start_new_thread (print_thread_hello, ()) + gdb.execute ("continue", release_gil=True) +except: + try: + _thread.start_new_thread (print_thread_hello, ()) + gdb.execute ("continue", release_gil=True) + except: + print ("Error: unable to start thread") + +while 1: + pass