Skip to content

Commit 45d4e84

Browse files
authored
[lldb] Update JSONTransport to use MainLoop for reading. (#152367)
Reapply "[lldb] Update JSONTransport to use MainLoop for reading." (#152155) This reverts commit cd40281. This also includes some updates to try to address the platforms with failing tests. I updated the JSONTransport and tests to use std::function instead of llvm:unique_function. I think the tests were failing due to the unique_function not being moved correctly in the loop on some platforms.
1 parent ff53086 commit 45d4e84

File tree

11 files changed

+593
-362
lines changed

11 files changed

+593
-362
lines changed

lldb/include/lldb/Host/JSONTransport.h

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@
1313
#ifndef LLDB_HOST_JSONTRANSPORT_H
1414
#define LLDB_HOST_JSONTRANSPORT_H
1515

16+
#include "lldb/Host/MainLoopBase.h"
17+
#include "lldb/Utility/IOObject.h"
18+
#include "lldb/Utility/Status.h"
1619
#include "lldb/lldb-forward.h"
1720
#include "llvm/ADT/StringRef.h"
1821
#include "llvm/Support/Error.h"
1922
#include "llvm/Support/FormatVariadic.h"
2023
#include "llvm/Support/JSON.h"
21-
#include <chrono>
24+
#include <string>
2225
#include <system_error>
26+
#include <vector>
2327

2428
namespace lldb_private {
2529

@@ -28,27 +32,26 @@ class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> {
2832
static char ID;
2933

3034
TransportEOFError() = default;
31-
32-
void log(llvm::raw_ostream &OS) const override {
33-
OS << "transport end of file reached";
34-
}
35-
std::error_code convertToErrorCode() const override {
36-
return llvm::inconvertibleErrorCode();
37-
}
35+
void log(llvm::raw_ostream &OS) const override;
36+
std::error_code convertToErrorCode() const override;
3837
};
3938

40-
class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> {
39+
class TransportUnhandledContentsError
40+
: public llvm::ErrorInfo<TransportUnhandledContentsError> {
4141
public:
4242
static char ID;
4343

44-
TransportTimeoutError() = default;
44+
explicit TransportUnhandledContentsError(std::string unhandled_contents);
4545

46-
void log(llvm::raw_ostream &OS) const override {
47-
OS << "transport operation timed out";
48-
}
49-
std::error_code convertToErrorCode() const override {
50-
return std::make_error_code(std::errc::timed_out);
46+
void log(llvm::raw_ostream &OS) const override;
47+
std::error_code convertToErrorCode() const override;
48+
49+
const std::string &getUnhandledContents() const {
50+
return m_unhandled_contents;
5151
}
52+
53+
private:
54+
std::string m_unhandled_contents;
5255
};
5356

5457
class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
@@ -57,17 +60,17 @@ class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
5760

5861
TransportInvalidError() = default;
5962

60-
void log(llvm::raw_ostream &OS) const override {
61-
OS << "transport IO object invalid";
62-
}
63-
std::error_code convertToErrorCode() const override {
64-
return std::make_error_code(std::errc::not_connected);
65-
}
63+
void log(llvm::raw_ostream &OS) const override;
64+
std::error_code convertToErrorCode() const override;
6665
};
6766

6867
/// A transport class that uses JSON for communication.
6968
class JSONTransport {
7069
public:
70+
using ReadHandleUP = MainLoopBase::ReadHandleUP;
71+
template <typename T>
72+
using Callback = std::function<void(MainLoopBase &, const llvm::Expected<T>)>;
73+
7174
JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output);
7275
virtual ~JSONTransport() = default;
7376

@@ -83,24 +86,68 @@ class JSONTransport {
8386
return WriteImpl(message);
8487
}
8588

86-
/// Reads the next message from the input stream.
89+
/// Registers the transport with the MainLoop.
8790
template <typename T>
88-
llvm::Expected<T> Read(const std::chrono::microseconds &timeout) {
89-
llvm::Expected<std::string> message = ReadImpl(timeout);
90-
if (!message)
91-
return message.takeError();
92-
return llvm::json::parse<T>(/*JSON=*/*message);
91+
llvm::Expected<ReadHandleUP> RegisterReadObject(MainLoopBase &loop,
92+
Callback<T> read_cb) {
93+
Status error;
94+
ReadHandleUP handle = loop.RegisterReadObject(
95+
m_input,
96+
[read_cb, this](MainLoopBase &loop) {
97+
char buf[kReadBufferSize];
98+
size_t num_bytes = sizeof(buf);
99+
if (llvm::Error error = m_input->Read(buf, num_bytes).takeError()) {
100+
read_cb(loop, std::move(error));
101+
return;
102+
}
103+
if (num_bytes)
104+
m_buffer.append(std::string(buf, num_bytes));
105+
106+
// If the buffer has contents, try parsing any pending messages.
107+
if (!m_buffer.empty()) {
108+
llvm::Expected<std::vector<std::string>> messages = Parse();
109+
if (llvm::Error error = messages.takeError()) {
110+
read_cb(loop, std::move(error));
111+
return;
112+
}
113+
114+
for (const auto &message : *messages)
115+
if constexpr (std::is_same<T, std::string>::value)
116+
read_cb(loop, message);
117+
else
118+
read_cb(loop, llvm::json::parse<T>(message));
119+
}
120+
121+
// On EOF, notify the callback after the remaining messages were
122+
// handled.
123+
if (num_bytes == 0) {
124+
if (m_buffer.empty())
125+
read_cb(loop, llvm::make_error<TransportEOFError>());
126+
else
127+
read_cb(loop, llvm::make_error<TransportUnhandledContentsError>(
128+
std::string(m_buffer)));
129+
}
130+
},
131+
error);
132+
if (error.Fail())
133+
return error.takeError();
134+
return handle;
93135
}
94136

95137
protected:
138+
template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
139+
Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
140+
}
96141
virtual void Log(llvm::StringRef message);
97142

98143
virtual llvm::Error WriteImpl(const std::string &message) = 0;
99-
virtual llvm::Expected<std::string>
100-
ReadImpl(const std::chrono::microseconds &timeout) = 0;
144+
virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
145+
146+
static constexpr size_t kReadBufferSize = 1024;
101147

102148
lldb::IOObjectSP m_input;
103149
lldb::IOObjectSP m_output;
150+
llvm::SmallString<kReadBufferSize> m_buffer;
104151
};
105152

106153
/// A transport class for JSON with a HTTP header.
@@ -111,14 +158,13 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
111158
virtual ~HTTPDelimitedJSONTransport() = default;
112159

113160
protected:
114-
virtual llvm::Error WriteImpl(const std::string &message) override;
115-
virtual llvm::Expected<std::string>
116-
ReadImpl(const std::chrono::microseconds &timeout) override;
117-
118-
// FIXME: Support any header.
119-
static constexpr llvm::StringLiteral kHeaderContentLength =
120-
"Content-Length: ";
121-
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n\r\n";
161+
llvm::Error WriteImpl(const std::string &message) override;
162+
llvm::Expected<std::vector<std::string>> Parse() override;
163+
164+
static constexpr llvm::StringLiteral kHeaderContentLength = "Content-Length";
165+
static constexpr llvm::StringLiteral kHeaderFieldSeparator = ":";
166+
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n";
167+
static constexpr llvm::StringLiteral kEndOfHeader = "\r\n\r\n";
122168
};
123169

124170
/// A transport class for JSON RPC.
@@ -129,9 +175,8 @@ class JSONRPCTransport : public JSONTransport {
129175
virtual ~JSONRPCTransport() = default;
130176

131177
protected:
132-
virtual llvm::Error WriteImpl(const std::string &message) override;
133-
virtual llvm::Expected<std::string>
134-
ReadImpl(const std::chrono::microseconds &timeout) override;
178+
llvm::Error WriteImpl(const std::string &message) override;
179+
llvm::Expected<std::vector<std::string>> Parse() override;
135180

136181
static constexpr llvm::StringLiteral kMessageSeparator = "\n";
137182
};

0 commit comments

Comments
 (0)