public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [patch] Implement post_event for Python scripts.
@ 2010-07-20 18:53 Phil Muldoon
  2010-07-20 19:08 ` Tom Tromey
                   ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Phil Muldoon @ 2010-07-20 18:53 UTC (permalink / raw)
  To: gdb-patches ml

This patch (ported from Archer) allows Python scripts to inject events
directly into GDB's event queue.  Each object inserted into the queue
is executed sequentially, but no guarantee is given when these events
will be executed (this is down to when GDB decides to process events
in the queue). This API allows multi-threaded Python scripts to
interact with GDB in a thread-safe manner (assuming it uses post-event
to execute Python code).

Comments?

Tested on x8664 with no regressions.

Cheers,

Phil

ChangeLog

2010-07-20  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.


Documentation ChangeLog:

2010-07-20  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (Basic Python): Describe post_event API.

Testsuite ChangeLog:

2010-07-20  Phil Muldoon  <pmuldoon@redhat.com>

	* gdb.python/python.exp (gdb_py_test_multiple): Add gdb.post_event
	tests.

--

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index be6cd3d..d47bcbe 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20490,6 +20490,21 @@ 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.
+@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 6680126..866eda5 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>
 
@@ -453,6 +454,113 @@ source_python_script (FILE *stream, const char *file)
 
 \f
 
+/* 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.  */
+      PyObject_CallObject (item->event, NULL);
+
+      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
@@ -759,6 +867,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 = []");
@@ -869,6 +978,9 @@ a boolean indicating if name is a field of the current implied argument\n\
 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/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp
index d0e6c63..e9dabf6 100644
--- a/gdb/testsuite/gdb.python/python.exp
+++ b/gdb/testsuite/gdb.python/python.exp
@@ -87,3 +87,17 @@ gdb_test "python import itertools; print 'IMPOR'+'TED'" "IMPORTED" "pythonX.Y/li
 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"

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2010-08-20  0:35 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-07-20 18:53 [patch] Implement post_event for Python scripts Phil Muldoon
2010-07-20 19:08 ` Tom Tromey
2010-07-27 16:30 ` Joel Brobecker
2010-07-28 14:18   ` Phil Muldoon
2010-07-28 17:31     ` Joel Brobecker
2010-07-30 21:43       ` Tom Tromey
2010-08-03 14:26         ` Phil Muldoon
2010-08-03 17:00           ` Eli Zaretskii
2010-08-11 18:35           ` Tom Tromey
2010-08-11 19:05             ` Joel Brobecker
2010-08-11 21:08             ` Phil Muldoon
2010-07-28 17:49     ` Eli Zaretskii
2010-08-18 13:46 ` Pedro Alves
2010-08-18 14:36   ` Daniel Jacobowitz
2010-08-18 17:39     ` Pedro Alves
2010-08-18 18:13   ` Tom Tromey
2010-08-20  0:35     ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).