diff options
author | Johan Sternerup <johan.sternerup@gmail.com> | 2024-06-01 18:16:30 +0200 |
---|---|---|
committer | Tom Tromey <tromey@adacore.com> | 2024-06-06 10:25:19 -0600 |
commit | 584dc32c594fa60eba0a3149001e8a844b09f5b9 (patch) | |
tree | 54496f1ae6e2fc2de6a5c5bccdcfb98463741837 | |
parent | 01469ac03e0673d40f8372d5e89139f86327e658 (diff) | |
download | gdb-584dc32c594fa60eba0a3149001e8a844b09f5b9.zip gdb-584dc32c594fa60eba0a3149001e8a844b09f5b9.tar.gz gdb-584dc32c594fa60eba0a3149001e8a844b09f5b9.tar.bz2 |
DAP: Allow for deferring stop events from gdb thread
The existing `send_event_later()` method allows commands processed on
the DAP thread to queue an event for execution until after the response
has been sent to the client.
We now introduce a corresponding method for use by the gdb thread. This
method `send_event_maybe_later()` will queue the event just like
`send_event_later()`, but only if it has been configured to do so by a
new @request option `defer_stop_events`. As the name implies the
functionality is currently only used for handling stop events.
Approved-By: Tom Tromey <tom@tromey.com>
-rw-r--r-- | gdb/python/lib/gdb/dap/events.py | 4 | ||||
-rw-r--r-- | gdb/python/lib/gdb/dap/server.py | 54 |
2 files changed, 51 insertions, 7 deletions
diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py index 276d314..80a259a 100644 --- a/gdb/python/lib/gdb/dap/events.py +++ b/gdb/python/lib/gdb/dap/events.py @@ -17,7 +17,7 @@ import gdb from .modules import is_module, make_module from .scopes import set_finish_value -from .server import send_event +from .server import send_event, send_event_maybe_later from .startup import exec_and_log, in_gdb_thread, log # True when the inferior is thought to be running, False otherwise. @@ -241,7 +241,7 @@ def _on_stop(event): global stop_reason_map obj["reason"] = stop_reason_map[event.details["reason"]] _expected_pause = False - send_event("stopped", obj) + send_event_maybe_later("stopped", obj) # This keeps a bit of state between the start of an inferior call and diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py index 7eb8717..8c6d908 100644 --- a/gdb/python/lib/gdb/dap/server.py +++ b/gdb/python/lib/gdb/dap/server.py @@ -124,6 +124,8 @@ class Server: self.in_stream = in_stream self.out_stream = out_stream self.child_stream = child_stream + self.delayed_events_lock = threading.Lock() + self.defer_stop_events = False self.delayed_events = [] # This queue accepts JSON objects that are then sent to the # DAP client. Writing is done in a separate thread to avoid @@ -177,9 +179,13 @@ class Server: log_stack() result["success"] = False result["message"] = str(e) - self.canceller.done(req) return result + @in_dap_thread + def _handle_command_finish(self, params): + req = params["seq"] + self.canceller.done(req) + # Read inferior output and sends OutputEvents to the client. It # is run in its own thread. def _read_inferior_output(self): @@ -239,8 +245,12 @@ class Server: break result = self._handle_command(cmd) self._send_json(result) - events = self.delayed_events - self.delayed_events = [] + self._handle_command_finish(cmd) + events = None + with self.delayed_events_lock: + events = self.delayed_events + self.delayed_events = [] + self.defer_stop_events = False for event, body in events: self.send_event(event, body) # Got the terminate request. This is handled by the @@ -254,7 +264,22 @@ class Server: def send_event_later(self, event, body=None): """Send a DAP event back to the client, but only after the current request has completed.""" - self.delayed_events.append((event, body)) + with self.delayed_events_lock: + self.delayed_events.append((event, body)) + + @in_gdb_thread + def send_event_maybe_later(self, event, body=None): + """Send a DAP event back to the client, but if a request is in-flight + within the dap thread and that request is configured to delay the event, + wait until the response has been sent until the event is sent back to + the client.""" + with self.canceller.lock: + if self.canceller.in_flight_dap_thread: + with self.delayed_events_lock: + if self.defer_stop_events: + self.delayed_events.append((event, body)) + return + self.send_event(event, body) # Note that this does not need to be run in any particular thread, # because it just creates an object and writes it to a thread-safe @@ -287,6 +312,15 @@ def send_event(event, body=None): _server.send_event(event, body) +def send_event_maybe_later(event, body=None): + """Send a DAP event back to the client, but if a request is in-flight + within the dap thread and that request is configured to delay the event, + wait until the response has been sent until the event is sent back to + the client.""" + global _server + _server.send_event_maybe_later(event, body) + + # A helper decorator that checks whether the inferior is running. def _check_not_running(func): @functools.wraps(func) @@ -307,7 +341,8 @@ def request( *, response: bool = True, on_dap_thread: bool = False, - expect_stopped: bool = True + expect_stopped: bool = True, + defer_stop_events: bool = False ): """A decorator for DAP requests. @@ -328,6 +363,10 @@ def request( fail with the 'notStopped' reason if it is processed while the inferior is running. When EXPECT_STOPPED is False, the request will proceed regardless of the inferior's state. + + If DEFER_STOP_EVENTS is True, then make sure any stop events sent + during the request processing are not sent to the client until the + response has been sent. """ # Validate the parameters. @@ -355,6 +394,11 @@ def request( func = in_gdb_thread(func) if response: + if defer_stop_events: + global _server + if _server is not None: + with _server.delayed_events_lock: + _server.defer_stop_events = True def sync_call(**args): return send_gdb_with_response(lambda: func(**args)) |