2010-08-11 Tom Tromey <tromey@redhat.com>
authorPhil Muldoon <pmuldoon@redhat.com>
Wed, 11 Aug 2010 20:54:12 +0000 (20:54 +0000)
committerPhil Muldoon <pmuldoon@redhat.com>
Wed, 11 Aug 2010 20:54:12 +0000 (20:54 +0000)
            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  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.python/python.exp (gdb_py_test_multiple): Add gdb.post_event
tests.

gdb/ChangeLog
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/python/python.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/python.exp

index 5098a78743eae8b28825f534b1e0fe1fac7d8a2b..2b548b7aadcbd97cf1c4f5cd6ff03e35ff036403 100644 (file)
@@ -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.
index 9cc366496ae7872662e36c8bc4c11e72609106eb..574515161e45ecc10c2fec4374b69cbe377fe79f 100644 (file)
@@ -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
index 9851212a005a139f0302b349c0246d1c4a420fc8..ba1607cf5d8bd6ebb6ed5314cbe9f3ff656d60e4 100644 (file)
@@ -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.
index 16c3cbaa9798403183cb9911a5aa064513b7740c..030b14205948f29bbf3ca8b36f971f83662b4337 100644 (file)
@@ -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)
 
 \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.  */
+      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." },
index 479e56386608dd83d353f3b59db30738de77b73f..e26a138f1e6a68ba5b5ab05346889fafb5bc3713 100644 (file)
@@ -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.
index e153ab81f4a1b0523b4b9b3c685432c5ddc65cd9..dc04911f1f31958f4a72bcb6dd69d1f6a0f071a5 100644 (file)
@@ -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