Skip to content

Commit 02417f5

Browse files
committed
[lldb] Update JSONTransport to use MainLoop for reading.
Reapply "[lldb] Update JSONTransport to use MainLoop for reading." (llvm#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 47258ca commit 02417f5

File tree

11 files changed

+573
-358
lines changed

11 files changed

+573
-358
lines changed

lldb/include/lldb/Host/JSONTransport.h

Lines changed: 84 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
#ifndef LLDB_HOST_JSONTRANSPORT_H
1414
#define LLDB_HOST_JSONTRANSPORT_H
1515

16+
#include "lldb/Host/MainLoopBase.h"
1617
#include "lldb/lldb-forward.h"
1718
#include "llvm/ADT/StringRef.h"
1819
#include "llvm/Support/Error.h"
1920
#include "llvm/Support/FormatVariadic.h"
2021
#include "llvm/Support/JSON.h"
21-
#include <chrono>
22+
#include <string>
2223
#include <system_error>
24+
#include <vector>
2325

2426
namespace lldb_private {
2527

@@ -28,27 +30,33 @@ class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> {
2830
static char ID;
2931

3032
TransportEOFError() = default;
31-
32-
void log(llvm::raw_ostream &OS) const override {
33-
OS << "transport end of file reached";
34-
}
33+
void log(llvm::raw_ostream &OS) const override { OS << "transport EOF"; }
3534
std::error_code convertToErrorCode() const override {
36-
return llvm::inconvertibleErrorCode();
35+
return std::make_error_code(std::errc::io_error);
3736
}
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)
45+
: m_unhandled_contents(unhandled_contents) {}
4546

4647
void log(llvm::raw_ostream &OS) const override {
47-
OS << "transport operation timed out";
48+
OS << "transport EOF with unhandled contents " << m_unhandled_contents;
4849
}
4950
std::error_code convertToErrorCode() const override {
50-
return std::make_error_code(std::errc::timed_out);
51+
return std::make_error_code(std::errc::bad_message);
5152
}
53+
54+
const std::string &getUnhandledContents() const {
55+
return m_unhandled_contents;
56+
}
57+
58+
private:
59+
std::string m_unhandled_contents;
5260
};
5361

5462
class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
@@ -68,6 +76,10 @@ class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
6876
/// A transport class that uses JSON for communication.
6977
class JSONTransport {
7078
public:
79+
using ReadHandleUP = MainLoopBase::ReadHandleUP;
80+
template <typename T>
81+
using Callback = std::function<void(MainLoopBase &, const llvm::Expected<T>)>;
82+
7183
JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output);
7284
virtual ~JSONTransport() = default;
7385

@@ -83,24 +95,69 @@ class JSONTransport {
8395
return WriteImpl(message);
8496
}
8597

86-
/// Reads the next message from the input stream.
98+
/// Registers the transport with the MainLoop.
8799
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);
100+
llvm::Expected<ReadHandleUP> RegisterReadObject(MainLoopBase &loop,
101+
Callback<T> callback) {
102+
Status error;
103+
ReadHandleUP handle = loop.RegisterReadObject(
104+
m_input,
105+
[&](MainLoopBase &loop) {
106+
char buffer[kReadBufferSize];
107+
size_t len = sizeof(buffer);
108+
if (llvm::Error error = m_input->Read(buffer, len).takeError()) {
109+
callback(loop, std::move(error));
110+
return;
111+
}
112+
113+
if (len)
114+
m_buffer.append(std::string(buffer, len));
115+
116+
// If the buffer has contents, try parsing any pending messages.
117+
if (!m_buffer.empty()) {
118+
llvm::Expected<std::vector<std::string>> messages = Parse();
119+
if (llvm::Error error = messages.takeError()) {
120+
callback(loop, std::move(error));
121+
return;
122+
}
123+
124+
for (const auto &message : *messages)
125+
if constexpr (std::is_same<T, std::string>::value)
126+
callback(loop, message);
127+
else
128+
callback(loop, llvm::json::parse<T>(message));
129+
}
130+
131+
// On EOF, notify the callback after the remaining messages were
132+
// handled.
133+
if (len == 0) {
134+
if (m_buffer.empty())
135+
callback(loop, llvm::make_error<TransportEOFError>());
136+
else
137+
callback(loop, llvm::make_error<TransportUnhandledContentsError>(
138+
m_buffer));
139+
}
140+
},
141+
error);
142+
if (error.Fail())
143+
return error.takeError();
144+
return handle;
93145
}
94146

95147
protected:
148+
template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
149+
Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
150+
}
96151
virtual void Log(llvm::StringRef message);
97152

98153
virtual llvm::Error WriteImpl(const std::string &message) = 0;
99-
virtual llvm::Expected<std::string>
100-
ReadImpl(const std::chrono::microseconds &timeout) = 0;
154+
virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
101155

102156
lldb::IOObjectSP m_input;
103157
lldb::IOObjectSP m_output;
158+
std::string m_buffer;
159+
160+
static constexpr size_t kReadBufferSize = 1024;
104161
};
105162

106163
/// A transport class for JSON with a HTTP header.
@@ -111,14 +168,13 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
111168
virtual ~HTTPDelimitedJSONTransport() = default;
112169

113170
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";
171+
llvm::Error WriteImpl(const std::string &message) override;
172+
llvm::Expected<std::vector<std::string>> Parse() override;
173+
174+
static constexpr llvm::StringLiteral kHeaderContentLength = "Content-Length";
175+
static constexpr llvm::StringLiteral kHeaderFieldSeparator = ":";
176+
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n";
177+
static constexpr llvm::StringLiteral kEndOfHeader = "\r\n\r\n";
122178
};
123179

124180
/// A transport class for JSON RPC.
@@ -129,9 +185,8 @@ class JSONRPCTransport : public JSONTransport {
129185
virtual ~JSONRPCTransport() = default;
130186

131187
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;
188+
llvm::Error WriteImpl(const std::string &message) override;
189+
llvm::Expected<std::vector<std::string>> Parse() override;
135190

136191
static constexpr llvm::StringLiteral kMessageSeparator = "\n";
137192
};

0 commit comments

Comments
 (0)