aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog8
-rw-r--r--gdb/doc/ChangeLog5
-rw-r--r--gdb/doc/gdb.texinfo39
-rw-r--r--gdb/python/python.c113
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.python/python.exp14
6 files changed, 184 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 5098a78..2b548b7 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,11 @@
+2010-08-11 Tom Tromey <tromey@redhat.com>
+ Phil Muldoon <pmuldoon@redhat.com>
+
+ * python/python.c (gdbpy_run_events): New function.
+ (gdbpy_post_event): Likewise.
+ (gdbpy_initialize_events): Likewise.
+ (_initialize_python): Call gdbpy_initialize_events.
+
2010-08-11 Ken Werner <ken.werner@de.ibm.com>
* gdb/valarith.c (vector_binop): New function.
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 9cc3664..5745151 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,8 @@
+2010-08-11 Tom Tromey <tromey@redhat.com>
+ Phil Muldoon <pmuldoon@redhat.com>
+
+ * gdb.texinfo (Basic Python): Describe post_event API.
+
2010-08-11 Phil Muldoon <pmuldoon@redhat.com>
* gdb.texinfo (Basic Python): Describe solib_address and
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9851212..ba1607c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20525,6 +20525,45 @@ compute values, for example, it is the only way to get the value of a
convenience variable (@pxref{Convenience Vars}) as a @code{gdb.Value}.
@end defun
+@findex gdb.post_event
+@defun post_event event
+Put @var{event}, a callable object taking no arguments, into
+@value{GDBN}'s internal event queue. This callable will be invoked at
+some later point, during @value{GDBN}'s event processing. Events
+posted using @code{post_event} will be run in the order in which they
+were posted; however, there is no way to know when they will be
+processed relative to other events inside @value{GDBN}.
+
+@value{GDBN} is not thread-safe. If your Python program uses multiple
+threads, you must be careful to only call @value{GDBN}-specific
+functions in the main @value{GDBN} thread. @code{post_event} ensures
+this. For example:
+
+@smallexample
+(@value{GDBP}) python
+>import threading
+>
+>class Writer():
+> def __init__(self, message):
+> self.message = message;
+> def __call__(self):
+> gdb.write(self.message)
+>
+>class MyThread1 (threading.Thread):
+> def run (self):
+> gdb.post_event(Writer("Hello "))
+>
+>class MyThread2 (threading.Thread):
+> def run (self):
+> gdb.post_event(Writer("World\n"))
+>
+>MyThread1().start()
+>MyThread2().start()
+>end
+(@value{GDBP}) Hello World
+@end smallexample
+@end defun
+
@findex gdb.write
@defun write string
Print a string to @value{GDBN}'s paginated standard output stream.
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 16c3cba..030b142 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -28,6 +28,7 @@
#include "value.h"
#include "language.h"
#include "exceptions.h"
+#include "event-loop.h"
#include <ctype.h>
@@ -548,6 +549,114 @@ source_python_script (FILE *stream, const char *file)
+/* Posting and handling events. */
+
+/* A single event. */
+struct gdbpy_event
+{
+ /* The Python event. This is just a callable object. */
+ PyObject *event;
+ /* The next event. */
+ struct gdbpy_event *next;
+};
+
+/* All pending events. */
+static struct gdbpy_event *gdbpy_event_list;
+/* The final link of the event list. */
+static struct gdbpy_event **gdbpy_event_list_end;
+
+/* We use a file handler, and not an async handler, so that we can
+ wake up the main thread even when it is blocked in poll(). */
+static int gdbpy_event_fds[2];
+
+/* The file handler callback. This reads from the internal pipe, and
+ then processes the Python event queue. This will always be run in
+ the main gdb thread. */
+static void
+gdbpy_run_events (int err, gdb_client_data ignore)
+{
+ struct cleanup *cleanup;
+ char buffer[100];
+ int r;
+
+ cleanup = ensure_python_env (get_current_arch (), current_language);
+
+ /* Just read whatever is available on the fd. It is relatively
+ harmless if there are any bytes left over. */
+ r = read (gdbpy_event_fds[0], buffer, sizeof (buffer));
+
+ while (gdbpy_event_list)
+ {
+ /* Dispatching the event might push a new element onto the event
+ loop, so we update here "atomically enough". */
+ struct gdbpy_event *item = gdbpy_event_list;
+ gdbpy_event_list = gdbpy_event_list->next;
+ if (gdbpy_event_list == NULL)
+ gdbpy_event_list_end = &gdbpy_event_list;
+
+ /* Ignore errors. */
+ if (PyObject_CallObject (item->event, NULL) == NULL)
+ PyErr_Clear ();
+
+ Py_DECREF (item->event);
+ xfree (item);
+ }
+
+ do_cleanups (cleanup);
+}
+
+/* Submit an event to the gdb thread. */
+static PyObject *
+gdbpy_post_event (PyObject *self, PyObject *args)
+{
+ struct gdbpy_event *event;
+ PyObject *func;
+ int wakeup;
+
+ if (!PyArg_ParseTuple (args, "O", &func))
+ return NULL;
+
+ if (!PyCallable_Check (func))
+ {
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Posted event is not callable"));
+ return NULL;
+ }
+
+ Py_INCREF (func);
+
+ /* From here until the end of the function, we have the GIL, so we
+ can operate on our global data structures without worrying. */
+ wakeup = gdbpy_event_list == NULL;
+
+ event = XNEW (struct gdbpy_event);
+ event->event = func;
+ event->next = NULL;
+ *gdbpy_event_list_end = event;
+ gdbpy_event_list_end = &event->next;
+
+ /* Wake up gdb when needed. */
+ if (wakeup)
+ {
+ char c = 'q'; /* Anything. */
+ if (write (gdbpy_event_fds[1], &c, 1) != 1)
+ return PyErr_SetFromErrno (PyExc_IOError);
+ }
+
+ Py_RETURN_NONE;
+}
+
+/* Initialize the Python event handler. */
+static void
+gdbpy_initialize_events (void)
+{
+ if (!pipe (gdbpy_event_fds))
+ {
+ gdbpy_event_list_end = &gdbpy_event_list;
+ add_file_handler (gdbpy_event_fds[0], gdbpy_run_events, NULL);
+ }
+}
+
/* Printing. */
/* A python function to write a single string using gdb's filtered
@@ -854,6 +963,7 @@ Enables or disables printing of Python stack traces."),
gdbpy_initialize_lazy_string ();
gdbpy_initialize_thread ();
gdbpy_initialize_inferior ();
+ gdbpy_initialize_events ();
PyRun_SimpleString ("import gdb");
PyRun_SimpleString ("gdb.pretty_printers = []");
@@ -974,6 +1084,9 @@ gdb.Symtab_and_line objects (or None)."},
Parse String as an expression, evaluate it, and return the result as a Value."
},
+ { "post_event", gdbpy_post_event, METH_VARARGS,
+ "Post an event into gdb's event loop." },
+
{ "target_charset", gdbpy_target_charset, METH_NOARGS,
"target_charset () -> string.\n\
Return the name of the current target charset." },
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 479e563..e26a138 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2010-08-11 Phil Muldoon <pmuldoon@redhat.com>
+
+ * gdb.python/python.exp (gdb_py_test_multiple): Add gdb.post_event
+ tests.
+
2010-08-11 Ken Werner <ken.werner@de.ibm.com>
* gdb.base/Makefile.in (EXECUTABLES): Add gnu_vector.
diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp
index e153ab8..dc04911 100644
--- a/gdb/testsuite/gdb.python/python.exp
+++ b/gdb/testsuite/gdb.python/python.exp
@@ -120,6 +120,20 @@ gdb_test_no_output \
"python x = gdb.execute('printf \"%d\", 23', to_string = True)"
gdb_test "python print x" "23"
+# Test post_event.
+gdb_py_test_multiple "post event insertion" \
+ "python" "" \
+ "someVal = 0" "" \
+ "class Foo():" "" \
+ " def __call__(self):" "" \
+ " global someVal" "" \
+ " someVal += 1" "" \
+ "gdb.post_event(Foo())" "" \
+ "end" ""
+
+gdb_test "python print someVal" "1" "test post event execution"
+gdb_test "python gdb.post_event(str(1))" "RuntimeError: Posted event is not callable.*" "Test non callable class"
+
# Test (no) pagination of the executed command.
gdb_test "show height" {Number of lines gdb thinks are in a page is unlimited\.}
set lines 10