aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohan Sternerup <johan.sternerup@gmail.com>2024-06-01 18:16:30 +0200
committerTom Tromey <tromey@adacore.com>2024-06-06 10:25:19 -0600
commit584dc32c594fa60eba0a3149001e8a844b09f5b9 (patch)
tree54496f1ae6e2fc2de6a5c5bccdcfb98463741837
parent01469ac03e0673d40f8372d5e89139f86327e658 (diff)
downloadgdb-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.py4
-rw-r--r--gdb/python/lib/gdb/dap/server.py54
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))