Files
binutils-gdb/gdb/python/lib/gdb/dap/io.py
Tom de Vries 7c34de9efd [gdb/dap] Join JSON writer thread with DAP thread
The DAP interpreter runs in its own thread, and starts a few threads:
- the JSON reader thread,
- the JSON writer thread, and
- the inferior output reader thread.

As part of the DAP shutdown, both the JSON reader thread and the JSON writer
thread, as well as the DAP main thread run to exit, but these exits are not
ordered in any way.

Wait in the main DAP thread for the exit of the JSON writer thread.

This makes sure that the responses are flushed to the DAP client before DAP
shutdown.

An earlier version of this patch used Queue.task_done() to accomplish the
same, but that didn't guarantee writing the "<thread name>: terminating"
log entry from thread_wrapper before DAP shutdown.

Tested on aarch64-linux.

Approved-By: Tom Tromey <tom@tromey.com>

PR dap/31380
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31380
2024-02-21 10:46:08 +01:00

83 lines
2.8 KiB
Python

# Copyright 2022-2024 Free Software Foundation, Inc.
# 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
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
from .startup import start_thread, send_gdb, log, log_stack, LogLevel
def read_json(stream):
"""Read a JSON-RPC message from STREAM.
The decoded object is returned.
None is returned on EOF."""
try:
# First read and parse the header.
content_length = None
while True:
line = stream.readline()
# If the line is empty, we hit EOF.
if len(line) == 0:
log("EOF")
return None
line = line.strip()
if line == b"":
break
if line.startswith(b"Content-Length:"):
line = line[15:].strip()
content_length = int(line)
continue
log("IGNORED: <<<%s>>>" % line)
data = bytes()
while len(data) < content_length:
new_data = stream.read(content_length - len(data))
# Maybe we hit EOF.
if len(new_data) == 0:
log("EOF after reading the header")
return None
data += new_data
return json.loads(data)
except OSError:
# Reading can also possibly throw an exception. Treat this as
# EOF.
log_stack(LogLevel.FULL)
return None
def start_json_writer(stream, queue):
"""Start the JSON writer thread.
It will read objects from QUEUE and write them to STREAM,
following the JSON-RPC protocol."""
def _json_writer():
seq = 1
while True:
obj = queue.get()
if obj is None:
# This is an exit request. The stream is already
# flushed, so all that's left to do is request an
# exit.
break
obj["seq"] = seq
seq = seq + 1
encoded = json.dumps(obj)
body_bytes = encoded.encode("utf-8")
header = "Content-Length: " + str(len(body_bytes)) + "\r\n\r\n"
header_bytes = header.encode("ASCII")
stream.write(header_bytes)
stream.write(body_bytes)
stream.flush()
return start_thread("JSON writer", _json_writer)