add thread enforcement decorators

This adds a couple of decorators that can be used to
mark functions as needing to be run in the gdb thread
or in the gtk thread.

This found at least one bug.
This commit is contained in:
Tom Tromey
2013-11-03 19:48:27 -07:00
parent ca645d2e80
commit b73212ab30
5 changed files with 36 additions and 12 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2012 Tom Tromey <tom@tromey.com>
# Copyright (C) 2012, 2013 Tom Tromey <tom@tromey.com>
# 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
@@ -21,6 +21,7 @@ import gui.toplevel
import gui.dprintf
import gui.events
import re
from gui.startup import in_gtk_thread
class GuiCommand(gdb.Command):
def __init__(self):

View File

@@ -17,13 +17,16 @@
import gdb
import gui.logwindow
from gui.startup import in_gdb_thread
class DPrintfBreakpoint(gdb.Breakpoint):
@in_gdb_thread
def __init__(self, spec, window, arg):
super(DPrintfBreakpoint, self).__init__(spec, gdb.BP_BREAKPOINT)
self.window = window
self.command = 'printf ' + arg
@in_gdb_thread
def stop(self):
window = self.window
if window is not None:

View File

@@ -14,6 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import gdb
from gui.startup import in_gdb_thread
class Invoker(object):
"""A simple class that can invoke a gdb command.
@@ -23,9 +24,11 @@ class Invoker(object):
self.cmd = cmd
# This is invoked in the gdb thread to run the command.
@in_gdb_thread
def do_call(self):
gdb.execute(self.cmd, from_tty = True, to_string = True)
# The object itself is the Gtk event handler.
# The object itself is the Gtk event handler -- though really this
# can be run in any thread.
def __call__(self, *args):
gdb.post_event(self.do_call)

View File

@@ -19,6 +19,7 @@ import gdb
from gui.invoker import Invoker
from gui.toplevel import Toplevel
import gui.startup
from gui.startup import in_gdb_thread, in_gtk_thread
import gui.toplevel
import gui.events
@@ -55,6 +56,7 @@ buffer_manager = BufferManager()
# Return (FILE, LINE) for the selected frame, or, if there is no
# frame, for "main". If there is no symbol file, return (None, None)
# instead.
@in_gdb_thread
def get_current_location():
try:
frame = gdb.selected_frame()
@@ -80,10 +82,7 @@ class LRUHandler:
def __init__(self):
self.windows = []
#
# These functions must run in the gdb thread.
#
@in_gdb_thread
def on_event(self, *args):
(frame, filename, lineno) = get_current_location()
if filename is not None:
@@ -91,6 +90,7 @@ class LRUHandler:
filename,
lineno))
@in_gdb_thread
def _connect_events(self):
# FIXME - we need an event for "selected frame changed".
# ... and thread-changed
@@ -98,14 +98,12 @@ class LRUHandler:
gdb.events.stop.connect(self.on_event)
gui.events.frame_changed.connect(self.on_event)
@in_gdb_thread
def _disconnect_events(self):
gdb.events.stop.disconnect(self.on_event)
gui.event.frame_changed.disconnect(self.on_event)
#
# These functions must run in the Gtk thread.
#
@in_gtk_thread
def pick_window(self, frame):
# If a window is showing FRAME, use it.
# Otherwise, if a window has no frame, use that.
@@ -123,6 +121,7 @@ class LRUHandler:
return no_frame
return self.windows[0]
@in_gtk_thread
def show_source(self, frame, srcfile, srcline):
w = self.pick_window(frame)
# LRU policy.
@@ -131,17 +130,19 @@ class LRUHandler:
w.frame = frame
w.show_source(srcfile, srcline)
@in_gtk_thread
def remove(self, window):
self.windows.remove(window)
if len(self.windows) == 0:
gui.startup.send_to_gtk(self._disconnect_events)
@in_gtk_thread
def add(self, window):
self.windows.insert(0, window)
if len(self.windows) == 1:
gui.startup.send_to_gtk(self._connect_events)
gdb.post_event(self._connect_events)
# Show something.
gui.startup.send_to_gtk(lambda: self.on_event(None))
gdb.post_event(lambda: self.on_event(None))
lru_handler = LRUHandler()

View File

@@ -47,13 +47,16 @@ class _GtkThread(threading.Thread):
GObject.type_register(GtkSource.View)
Gtk.main()
_gdb_thread = None
_t = None
def start_gtk():
global _t
global _gdb_thread
if _t is None:
GObject.threads_init()
Gdk.threads_init()
_gdb_thread = threading.current_thread()
_t = _GtkThread()
_t.setDaemon(True)
_t.start()
@@ -64,3 +67,16 @@ def create_builder(filename):
builder.add_from_file(os.path.join(gui.self_dir, filename))
return builder
def in_gdb_thread(func):
def ensure_gdb_thread(*args, **kwargs):
if threading.current_thread() is not _gdb_thread:
raise RuntimeError("must run '%s' in gdb thread" % repr(func))
return func(*args, **kwargs)
return ensure_gdb_thread
def in_gtk_thread(func):
def ensure_gtk_thread(*args, **kwargs):
if threading.current_thread() is not _t:
raise RuntimeError("must run '%s' in Gtk thread" % repr(func))
return func(*args, **kwargs)
return ensure_gtk_thread