forked from Imagelibrary/binutils-gdb
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>
This commit is contained in:
committed by
Tom Tromey
parent
01469ac03e
commit
584dc32c59
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user