summaryrefslogtreecommitdiffstats
path: root/Source/cmDebuggerPipeConnection.h
blob: 0991ff7c17b7da04b42ecfd28144408fadc27de0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#pragma once

#include "cmConfigure.h" // IWYU pragma: keep

#include <condition_variable>
#include <cstddef>
#include <future>
#include <memory>
#include <mutex>
#include <string>
#include <thread>

#include <cm3p/cppdap/io.h>
#include <cm3p/uv.h>

#include "cmDebuggerAdapter.h"
#include "cmUVHandlePtr.h"

namespace cmDebugger {

class cmDebuggerPipeBase : public dap::ReaderWriter
{
public:
  cmDebuggerPipeBase(std::string name);

  void WaitForConnection();

  // dap::ReaderWriter implementation

  void close() final;
  size_t read(void* buffer, size_t n) final;
  bool write(const void* buffer, size_t n) final;

protected:
  virtual void CloseConnection(){};
  template <typename T>
  void StartReading(uv_stream_t* stream)
  {
    uv_read_start(
      stream,
      // alloc_cb
      [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
        (void)handle;
        char* rawBuffer = new char[suggested_size];
        *buf =
          uv_buf_init(rawBuffer, static_cast<unsigned int>(suggested_size));
      },
      // read_cb
      [](uv_stream_t* readStream, ssize_t nread, const uv_buf_t* buf) {
        auto conn = static_cast<T*>(readStream->data);
        if (conn) {
          if (nread >= 0) {
            conn->BufferData(std::string(buf->base, buf->base + nread));
          } else {
            conn->close();
          }
        }
        delete[] (buf->base);
      });
  }
  void StopLoop();

  const std::string PipeName;
  std::thread LoopThread;
  cm::uv_loop_ptr Loop;
  cm::uv_pipe_ptr Pipe;
  std::mutex Mutex;
  std::condition_variable Connected;
  bool FailedToOpen = false;

private:
  void BufferData(const std::string& data);
  void WriteInternal();

  cm::uv_async_ptr LoopExit;
  cm::uv_async_ptr WriteEvent;
  cm::uv_async_ptr PipeClose;
  std::string WriteBuffer;
  std::string ReadBuffer;
  std::condition_variable ReadReady;
  std::condition_variable WriteComplete;
};

class cmDebuggerPipeConnection
  : public cmDebuggerPipeBase
  , public cmDebuggerConnection
  , public std::enable_shared_from_this<cmDebuggerPipeConnection>
{
public:
  cmDebuggerPipeConnection(std::string name);
  ~cmDebuggerPipeConnection() override;

  void WaitForConnection() override
  {
    cmDebuggerPipeBase::WaitForConnection();
  }

  bool StartListening(std::string& errorMessage) override;
  std::shared_ptr<dap::Reader> GetReader() override;
  std::shared_ptr<dap::Writer> GetWriter() override;

  // dap::ReaderWriter implementation

  bool isOpen() override;

  // Used for unit test synchronization
  std::promise<void> StartedListening;

private:
  void CloseConnection() override;
  void Connect(uv_stream_t* server);

  cm::uv_pipe_ptr ServerPipe;
  cm::uv_async_ptr ServerPipeClose;
};

class cmDebuggerPipeClient : public cmDebuggerPipeBase
{
public:
  using cmDebuggerPipeBase::cmDebuggerPipeBase;
  ~cmDebuggerPipeClient() override;

  void Start();

  // dap::ReaderWriter implementation

  bool isOpen() override;

private:
  void CloseConnection() override;
  void Connect();
  void FailConnection();

  bool IsConnected = false;
};

} // namespace cmDebugger