mirror of
https://github.com/tromey/gdb-gui.git
synced 2026-01-04 15:40:06 +01:00
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:
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user