================
@@ -54,112 +50,227 @@ class TransportUnhandledContentsError
   std::string m_unhandled_contents;
 };
 
-class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
+/// A transport is responsible for maintaining the connection to a client
+/// application, and reading/writing structured messages to it.
+///
+/// Transports have limited thread safety requirements:
+///  - Messages will not be sent concurrently.
+///  - Messages MAY be sent while Run() is reading, or its callback is active.
+template <typename Req, typename Resp, typename Evt> class Transport {
 public:
-  static char ID;
-
-  TransportInvalidError() = default;
+  using Message = std::variant<Req, Resp, Evt>;
+
+  virtual ~Transport() = default;
+
+  // Called by transport to send outgoing messages.
+  virtual void Event(const Evt &) = 0;
+  virtual void Request(const Req &) = 0;
+  virtual void Response(const Resp &) = 0;
+
+  /// Implemented to handle incoming messages. (See Run() below).
+  class MessageHandler {
+  public:
+    virtual ~MessageHandler() = default;
+    virtual void OnEvent(const Evt &) = 0;
+    virtual void OnRequest(const Req &) = 0;
+    virtual void OnResponse(const Resp &) = 0;
+  };
+
+  /// Called by server or client to receive messages from the connection.
+  /// The transport should in turn invoke the handler to process messages.
+  /// The MainLoop is used to handle reading from the incoming connection and
+  /// will run until the loop is terminated.
+  virtual llvm::Error Run(MainLoop &, MessageHandler &) = 0;
 
-  void log(llvm::raw_ostream &OS) const override;
-  std::error_code convertToErrorCode() const override;
+protected:
+  template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
+    Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
+  }
+  virtual void Log(llvm::StringRef message) = 0;
 };
 
-/// A transport class that uses JSON for communication.
-class JSONTransport {
+/// A JSONTransport will encode and decode messages using JSON.
+template <typename Req, typename Resp, typename Evt>
+class JSONTransport : public Transport<Req, Resp, Evt> {
 public:
-  using ReadHandleUP = MainLoopBase::ReadHandleUP;
-  template <typename T>
-  using Callback = std::function<void(MainLoopBase &, const 
llvm::Expected<T>)>;
-
-  JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output);
-  virtual ~JSONTransport() = default;
-
-  /// Transport is not copyable.
-  /// @{
-  JSONTransport(const JSONTransport &rhs) = delete;
-  void operator=(const JSONTransport &rhs) = delete;
-  /// @}
-
-  /// Writes a message to the output stream.
-  template <typename T> llvm::Error Write(const T &t) {
-    const std::string message = llvm::formatv("{0}", toJSON(t)).str();
-    return WriteImpl(message);
+  using Transport<Req, Resp, Evt>::Transport;
+
+  JSONTransport(lldb::IOObjectSP in, lldb::IOObjectSP out)
+      : m_in(in), m_out(out) {}
+
+  void Event(const Evt &evt) override { Write(evt); }
+  void Request(const Req &req) override { Write(req); }
+  void Response(const Resp &resp) override { Write(resp); }
+
+  /// Run registers the transport with the given MainLoop and handles any
+  /// incoming messages using the given MessageHandler.
+  llvm::Error
+  Run(MainLoop &loop,
+      typename Transport<Req, Resp, Evt>::MessageHandler &handler) override {
+    llvm::Error error = llvm::Error::success();
+    Status status;
+    auto read_handle = loop.RegisterReadObject(
+        m_in,
+        std::bind(&JSONTransport::OnRead, this, &error, std::placeholders::_1,
+                  std::ref(handler)),
+        status);
+    if (status.Fail()) {
+      // This error is only set if the read object handler is invoked, mark it
+      // as consumed if registration of the handler failed.
+      llvm::consumeError(std::move(error));
+      return status.takeError();
+    }
+
+    status = loop.Run();
----------------
labath wrote:

This kind of goes against the idea that the main loop will be run in the "main" 
function. Would it be possible to structure code so that this function (which 
then wouldn't be called `Run`) only registers the callback, and `Run`ning 
happens in the caller. If it's particularly common that the loop will only be 
loop will be only run with a single handler, maybe you could make a wrapper 
function (which then doesn't need to take the MainLoop argument) that takes 
invokes this function and then calles `Run` -- similar to how there are 
synchronous and asynchronous versions of `Socket::Accept`

https://github.com/llvm/llvm-project/pull/153121
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to