提交 df0a32f4 编写于 作者: B Brad King 提交者: Kitware Robot

Merge topic 'debugger-pipe-connections'

8b1257e7 Debugger: Replace libuv with platform-specific connection code
Acked-by: NKitware Robot <kwrobot@kitware.com>
Acked-by: Nbuildbot <buildbot@kitware.com>
Merge-request: !8711
......@@ -783,8 +783,6 @@ if(CMake_ENABLE_DEBUGGER)
cmDebuggerBreakpointManager.h
cmDebuggerExceptionManager.cxx
cmDebuggerExceptionManager.h
cmDebuggerPipeConnection.cxx
cmDebuggerPipeConnection.h
cmDebuggerProtocol.cxx
cmDebuggerProtocol.h
cmDebuggerSourceBreakpoint.cxx
......@@ -802,6 +800,21 @@ if(CMake_ENABLE_DEBUGGER)
cmDebuggerVariablesManager.cxx
cmDebuggerVariablesManager.h
)
if(WIN32)
target_sources(
CMakeLib
PRIVATE
cmDebuggerWindowsPipeConnection.cxx
cmDebuggerWindowsPipeConnection.h
)
else()
target_sources(
CMakeLib
PRIVATE
cmDebuggerPosixPipeConnection.cxx
cmDebuggerPosixPipeConnection.h
)
endif()
target_link_libraries(CMakeLib PUBLIC cppdap::cppdap)
endif()
......@@ -953,7 +966,6 @@ if(CMake_BUILD_PCH)
"$<$<COMPILE_LANGUAGE:CXX>:cmArgumentParser.h>"
"$<$<COMPILE_LANGUAGE:CXX>:cmake.h>"
"$<$<COMPILE_LANGUAGE:CXX>:cmCMakePath.h>"
"$<$<COMPILE_LANGUAGE:CXX>:cmDebuggerPipeConnection.h>"
"$<$<COMPILE_LANGUAGE:CXX>:cmCurl.h>")
set_source_files_properties(
......
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmDebuggerPipeConnection.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <utility>
namespace cmDebugger {
struct write_req_t
{
uv_write_t req;
uv_buf_t buf;
};
cmDebuggerPipeBase::cmDebuggerPipeBase(std::string name)
: PipeName(std::move(name))
{
Loop.init();
LoopExit.init(
*Loop, [](uv_async_t* handle) { uv_stop((uv_loop_t*)handle->data); },
Loop);
WriteEvent.init(
*Loop,
[](uv_async_t* handle) {
auto* conn = static_cast<cmDebuggerPipeBase*>(handle->data);
conn->WriteInternal();
},
this);
PipeClose.init(
*Loop,
[](uv_async_t* handle) {
auto* conn = static_cast<cmDebuggerPipeBase*>(handle->data);
if (conn->Pipe.get()) {
conn->Pipe->data = nullptr;
conn->Pipe.reset();
}
},
this);
}
void cmDebuggerPipeBase::WaitForConnection()
{
std::unique_lock<std::mutex> lock(Mutex);
Connected.wait(lock, [this] { return isOpen() || FailedToOpen; });
if (FailedToOpen) {
throw std::runtime_error("Failed to open debugger connection.");
}
}
void cmDebuggerPipeBase::close()
{
std::unique_lock<std::mutex> lock(Mutex);
CloseConnection();
PipeClose.send();
lock.unlock();
ReadReady.notify_all();
}
size_t cmDebuggerPipeBase::read(void* buffer, size_t n)
{
std::unique_lock<std::mutex> lock(Mutex);
ReadReady.wait(lock, [this] { return !isOpen() || !ReadBuffer.empty(); });
if (!isOpen() && ReadBuffer.empty()) {
return 0;
}
auto size = std::min(n, ReadBuffer.size());
memcpy(buffer, ReadBuffer.data(), size);
ReadBuffer.erase(0, size);
return size;
}
bool cmDebuggerPipeBase::write(const void* buffer, size_t n)
{
std::unique_lock<std::mutex> lock(Mutex);
WriteBuffer.append(static_cast<const char*>(buffer), n);
lock.unlock();
WriteEvent.send();
lock.lock();
WriteComplete.wait(lock, [this] { return WriteBuffer.empty(); });
return true;
}
void cmDebuggerPipeBase::StopLoop()
{
LoopExit.send();
if (LoopThread.joinable()) {
LoopThread.join();
}
}
void cmDebuggerPipeBase::BufferData(const std::string& data)
{
std::unique_lock<std::mutex> lock(Mutex);
ReadBuffer += data;
lock.unlock();
ReadReady.notify_all();
}
void cmDebuggerPipeBase::WriteInternal()
{
std::unique_lock<std::mutex> lock(Mutex);
auto n = WriteBuffer.length();
assert(this->Pipe.get());
write_req_t* req = new write_req_t;
req->req.data = &WriteComplete;
char* rawBuffer = new char[n];
req->buf = uv_buf_init(rawBuffer, static_cast<unsigned int>(n));
memcpy(req->buf.base, WriteBuffer.data(), n);
WriteBuffer.clear();
lock.unlock();
uv_write(
reinterpret_cast<uv_write_t*>(req), this->Pipe, &req->buf, 1,
[](uv_write_t* cb_req, int status) {
(void)status; // We need to free memory even if the write failed.
write_req_t* wr = reinterpret_cast<write_req_t*>(cb_req);
reinterpret_cast<std::condition_variable*>(wr->req.data)->notify_all();
delete[] (wr->buf.base);
delete wr;
});
#ifdef __clang_analyzer__
// Tell clang-analyzer that 'rawBuffer' does not leak.
// We pass ownership to the closure.
delete[] rawBuffer;
#endif
}
cmDebuggerPipeConnection::cmDebuggerPipeConnection(std::string name)
: cmDebuggerPipeBase(std::move(name))
{
ServerPipeClose.init(
*Loop,
[](uv_async_t* handle) {
auto* conn = static_cast<cmDebuggerPipeConnection*>(handle->data);
if (conn->ServerPipe.get()) {
conn->ServerPipe->data = nullptr;
conn->ServerPipe.reset();
}
},
this);
}
cmDebuggerPipeConnection::~cmDebuggerPipeConnection()
{
StopLoop();
}
bool cmDebuggerPipeConnection::StartListening(std::string& errorMessage)
{
this->ServerPipe.init(*Loop, 0,
static_cast<cmDebuggerPipeConnection*>(this));
int r;
if ((r = uv_pipe_bind(this->ServerPipe, this->PipeName.c_str())) != 0) {
errorMessage =
"Internal Error with " + this->PipeName + ": " + uv_err_name(r);
return false;
}
r = uv_listen(this->ServerPipe, 1, [](uv_stream_t* stream, int status) {
if (status >= 0) {
auto* conn = static_cast<cmDebuggerPipeConnection*>(stream->data);
if (conn) {
conn->Connect(stream);
}
}
});
if (r != 0) {
errorMessage =
"Internal Error listening on " + this->PipeName + ": " + uv_err_name(r);
return false;
}
// Start the libuv event loop thread so that a client can connect.
LoopThread = std::thread([this] { uv_run(Loop, UV_RUN_DEFAULT); });
StartedListening.set_value();
return true;
}
std::shared_ptr<dap::Reader> cmDebuggerPipeConnection::GetReader()
{
return std::static_pointer_cast<dap::Reader>(shared_from_this());
}
std::shared_ptr<dap::Writer> cmDebuggerPipeConnection::GetWriter()
{
return std::static_pointer_cast<dap::Writer>(shared_from_this());
}
bool cmDebuggerPipeConnection::isOpen()
{
return this->Pipe.get() != nullptr;
}
void cmDebuggerPipeConnection::CloseConnection()
{
ServerPipeClose.send();
}
void cmDebuggerPipeConnection::Connect(uv_stream_t* server)
{
if (this->Pipe.get()) {
// Accept and close all pipes but the first:
cm::uv_pipe_ptr rejectPipe;
rejectPipe.init(*Loop, 0);
uv_accept(server, rejectPipe);
return;
}
cm::uv_pipe_ptr ClientPipe;
ClientPipe.init(*Loop, 0, static_cast<cmDebuggerPipeConnection*>(this));
if (uv_accept(server, ClientPipe) != 0) {
return;
}
StartReading<cmDebuggerPipeConnection>(ClientPipe);
std::unique_lock<std::mutex> lock(Mutex);
Pipe = std::move(ClientPipe);
lock.unlock();
Connected.notify_all();
}
cmDebuggerPipeClient::~cmDebuggerPipeClient()
{
StopLoop();
}
void cmDebuggerPipeClient::Start()
{
this->Pipe.init(*Loop, 0, static_cast<cmDebuggerPipeClient*>(this));
uv_connect_t* connect = new uv_connect_t;
connect->data = this;
uv_pipe_connect(
connect, Pipe, PipeName.c_str(), [](uv_connect_t* cb_connect, int status) {
auto* conn = static_cast<cmDebuggerPipeClient*>(cb_connect->data);
if (status >= 0) {
conn->Connect();
} else {
conn->FailConnection();
}
delete cb_connect;
});
// Start the libuv event loop so that the pipe can connect.
LoopThread = std::thread([this] { uv_run(Loop, UV_RUN_DEFAULT); });
}
bool cmDebuggerPipeClient::isOpen()
{
return IsConnected;
}
void cmDebuggerPipeClient::CloseConnection()
{
IsConnected = false;
}
void cmDebuggerPipeClient::Connect()
{
StartReading<cmDebuggerPipeClient>(Pipe);
std::unique_lock<std::mutex> lock(Mutex);
IsConnected = true;
lock.unlock();
Connected.notify_all();
}
void cmDebuggerPipeClient::FailConnection()
{
std::unique_lock<std::mutex> lock(Mutex);
FailedToOpen = true;
lock.unlock();
Connected.notify_all();
}
} // namespace cmDebugger
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmDebuggerPosixPipeConnection.h"
#include <cerrno>
#include <cstring>
#include <stdexcept>
#include <utility>
#include <unistd.h>
#include <sys/socket.h>
namespace cmDebugger {
#ifndef _WIN32
cmDebuggerPipeConnection_POSIX::cmDebuggerPipeConnection_POSIX(
std::string name)
: PipeName(std::move(name))
{
addr.sun_path[0] = '\0';
}
cmDebuggerPipeConnection_POSIX::~cmDebuggerPipeConnection_POSIX()
{
if (isOpen()) {
close();
}
}
bool cmDebuggerPipeConnection_POSIX::StartListening(std::string& errorMessage)
{
listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (listen_fd < 0) {
errorMessage = "Failed to create socket: ";
errorMessage += strerror(errno);
return false;
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, PipeName.c_str(), sizeof(addr.sun_path));
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
if (bind(listen_fd, (sockaddr*)&addr, sizeof(addr)) == -1) {
errorMessage = "Failed to bind name '";
errorMessage += addr.sun_path;
errorMessage += "' to socket: ";
errorMessage += strerror(errno);
close_listen();
return false;
}
if (listen(listen_fd, 1) == -1) {
errorMessage = "Failed to listen on socket: ";
errorMessage += strerror(errno);
close_listen();
return false;
}
StartedListening.set_value();
return true;
}
std::shared_ptr<dap::Reader> cmDebuggerPipeConnection_POSIX::GetReader()
{
return std::static_pointer_cast<dap::Reader>(shared_from_this());
}
std::shared_ptr<dap::Writer> cmDebuggerPipeConnection_POSIX::GetWriter()
{
return std::static_pointer_cast<dap::Writer>(shared_from_this());
}
bool cmDebuggerPipeConnection_POSIX::isOpen()
{
return rw_pipe >= 0;
}
void cmDebuggerPipeConnection_POSIX::close()
{
close_listen();
::close(rw_pipe);
rw_pipe = -1;
}
void cmDebuggerPipeConnection_POSIX::close_listen()
{
if (strlen(addr.sun_path) > 0) {
unlink(addr.sun_path);
addr.sun_path[0] = '\0';
}
::close(listen_fd);
listen_fd = -1;
}
void cmDebuggerPipeConnection_POSIX::WaitForConnection()
{
sockaddr_un laddr;
socklen_t len = sizeof(laddr);
rw_pipe = accept(listen_fd, (sockaddr*)&laddr, &len);
if (rw_pipe < 0) {
close();
return;
}
close_listen(); // no longer need the listen resources
}
size_t cmDebuggerPipeConnection_POSIX::read(void* buffer, size_t n)
{
size_t result = 0;
if (rw_pipe >= 0) {
result = ::read(rw_pipe, buffer, n);
if (result == 0) {
close();
}
}
return result;
}
bool cmDebuggerPipeConnection_POSIX::write(void const* buffer, size_t n)
{
bool result = false;
if (rw_pipe >= 0) {
result = ::write(rw_pipe, buffer, n) >= 0;
if (!result) {
close();
}
}
return result;
}
cmDebuggerPipeClient_POSIX::cmDebuggerPipeClient_POSIX(std::string name)
: PipeName(std::move(name))
{
}
cmDebuggerPipeClient_POSIX::~cmDebuggerPipeClient_POSIX()
{
close();
}
void cmDebuggerPipeClient_POSIX::WaitForConnection()
{
rw_pipe = socket(AF_UNIX, SOCK_STREAM, 0);
if (rw_pipe < 0) {
throw std::runtime_error(std::string("Failed to create socket: ") +
strerror(errno));
}
sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, PipeName.c_str(), sizeof(addr.sun_path));
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
if (connect(rw_pipe, (sockaddr*)&addr, sizeof(addr)) == -1) {
close();
throw std::runtime_error(
std::string("Failed to connect path to socket: ") + strerror(errno));
}
}
bool cmDebuggerPipeClient_POSIX::isOpen()
{
return rw_pipe >= 0;
}
void cmDebuggerPipeClient_POSIX::close()
{
if (isOpen()) {
::close(rw_pipe);
rw_pipe = -1;
}
}
size_t cmDebuggerPipeClient_POSIX::read(void* buffer, size_t n)
{
int count = 0;
if (isOpen()) {
count = static_cast<int>(::read(rw_pipe, buffer, n));
if (count == 0) {
close();
}
}
return count;
}
bool cmDebuggerPipeClient_POSIX::write(void const* buffer, size_t n)
{
int count = 0;
if (isOpen()) {
count = static_cast<int>(::write(rw_pipe, buffer, n));
if (count < 0) {
close();
}
}
return count > 0;
}
#endif // !_WIN32
} // namespace cmDebugger
/* 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 <cstddef>
#include <future>
#include <memory>
#include <string>
#include <cm3p/cppdap/io.h>
#include <sys/un.h>
#include "cmDebuggerAdapter.h"
namespace cmDebugger {
#ifndef _WIN32
class cmDebuggerPipeConnection_POSIX
: public dap::ReaderWriter
, public cmDebuggerConnection
, public std::enable_shared_from_this<cmDebuggerPipeConnection_POSIX>
{
public:
cmDebuggerPipeConnection_POSIX(std::string name);
~cmDebuggerPipeConnection_POSIX() override;
void WaitForConnection() override;
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;
void close() override;
size_t read(void* buffer, size_t n) override;
bool write(void const* buffer, size_t n) override;
// Used for unit test synchronization
std::promise<void> StartedListening;
private:
void close_listen(); // release listen resources
std::string const PipeName;
sockaddr_un addr;
int listen_fd = -1; // listen fd
int rw_pipe = -1; // rw fd
};
using cmDebuggerPipeConnection = cmDebuggerPipeConnection_POSIX;
class cmDebuggerPipeClient_POSIX
: public dap::ReaderWriter
, public std::enable_shared_from_this<cmDebuggerPipeClient_POSIX>
{
public:
cmDebuggerPipeClient_POSIX(std::string name);
~cmDebuggerPipeClient_POSIX() override;
void WaitForConnection();
bool isOpen() override;
void close() override;
size_t read(void* buffer, size_t n) override;
bool write(void const* buffer, size_t n) override;
private:
std::string const PipeName;
int rw_pipe = -1;
};
using cmDebuggerPipeClient = cmDebuggerPipeClient_POSIX;
#endif // !_WIN32
} // namespace cmDebugger
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmDebuggerWindowsPipeConnection.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <utility>
namespace cmDebugger {
#ifdef _WIN32
DuplexPipe_WIN32::DuplexPipe_WIN32(HANDLE pipe)
: hPipe(pipe)
{
readOp.Offset = readOp.OffsetHigh = 0;
readOp.hEvent = CreateEvent(NULL, true, false, NULL);
writeOp.Offset = readOp.OffsetHigh = 0;
writeOp.hEvent = CreateEvent(NULL, true, false, NULL);
}
DuplexPipe_WIN32::~DuplexPipe_WIN32()
{
close();
}
size_t DuplexPipe_WIN32::read(void* buffer, size_t n)
{
if (hPipe != INVALID_HANDLE_VALUE) {
readOp.Offset = readOp.OffsetHigh = 0;
ResetEvent(readOp.hEvent);
auto r = ReadFile(hPipe, buffer, n, NULL, &readOp);
auto err = GetLastError();
if (r || err == ERROR_IO_PENDING) {
DWORD nRead = 0;
if (GetOverlappedResult(hPipe, &readOp, &nRead, true)) {
return nRead;
}
}
}
return 0;
}
bool DuplexPipe_WIN32::write(void const* buffer, size_t n)
{
if (hPipe != INVALID_HANDLE_VALUE) {
writeOp.Offset = writeOp.OffsetHigh = 0;
ResetEvent(writeOp.hEvent);
auto w = WriteFile(hPipe, buffer, n, NULL, &writeOp);
auto err = GetLastError();
if (w || err == ERROR_IO_PENDING) {
DWORD nWrite = 0;
if (GetOverlappedResult(hPipe, &writeOp, &nWrite, true)) {
return n == nWrite;
}
}
}
return false;
}
void DuplexPipe_WIN32::close()
{
CloseHandle(hPipe);
hPipe = INVALID_HANDLE_VALUE;
CloseHandle(readOp.hEvent);
CloseHandle(writeOp.hEvent);
readOp.hEvent = writeOp.hEvent = INVALID_HANDLE_VALUE;
}
bool DuplexPipe_WIN32::WaitForConnection()
{
auto connect = ConnectNamedPipe(hPipe, &readOp);
auto err = GetLastError();
if (!connect && err == ERROR_IO_PENDING) {
DWORD ignored;
if (GetOverlappedResult(hPipe, &readOp, &ignored, true)) {
return true;
}
}
return connect || err == ERROR_PIPE_CONNECTED;
}
cmDebuggerPipeConnection_WIN32::cmDebuggerPipeConnection_WIN32(
std::string name)
: PipeName(std::move(name))
, pipes(nullptr)
{
}
cmDebuggerPipeConnection_WIN32::~cmDebuggerPipeConnection_WIN32()
{
if (isOpen()) {
pipes = nullptr;
}
}
bool cmDebuggerPipeConnection_WIN32::StartListening(std::string& errorMessage)
{
bool result = true;
auto hPipe = CreateNamedPipeA(
PipeName.c_str(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1,
1024 * 16, 1024 * 16, NMPWAIT_USE_DEFAULT_WAIT, NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
auto err = GetLastError();
errorMessage = GetErrorMessage(err);
result = false;
}
if (result) {
pipes = std::make_unique<DuplexPipe_WIN32>(hPipe);
}
StartedListening.set_value();
return result;
}
std::string cmDebuggerPipeConnection_WIN32::GetErrorMessage(DWORD errorCode)
{
LPSTR message = nullptr;
DWORD size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&message, 0, nullptr);
std::string errorMessage = "Internal Error with " + this->PipeName + ": " +
std::string(message, size);
LocalFree(message);
return errorMessage;
}
std::shared_ptr<dap::Reader> cmDebuggerPipeConnection_WIN32::GetReader()
{
return std::static_pointer_cast<dap::Reader>(shared_from_this());
}
std::shared_ptr<dap::Writer> cmDebuggerPipeConnection_WIN32::GetWriter()
{
return std::static_pointer_cast<dap::Writer>(shared_from_this());
}
bool cmDebuggerPipeConnection_WIN32::isOpen()
{
return pipes != nullptr;
}
void cmDebuggerPipeConnection_WIN32::close()
{
CloseConnection();
}
void cmDebuggerPipeConnection_WIN32::CloseConnection()
{
if (isOpen()) {
pipes->close();
pipes = nullptr;
}
}
void cmDebuggerPipeConnection_WIN32::WaitForConnection()
{
if (!isOpen()) {
return;
}
if (pipes->WaitForConnection()) {
return;
}
CloseConnection();
}
size_t cmDebuggerPipeConnection_WIN32::read(void* buffer, size_t n)
{
size_t result = 0;
if (isOpen()) {
result = pipes->read(buffer, n);
if (result == 0) {
CloseConnection();
}
}
return result;
}
bool cmDebuggerPipeConnection_WIN32::write(void const* buffer, size_t n)
{
bool result = false;
if (isOpen()) {
result = pipes->write(buffer, n);
if (!result) {
CloseConnection();
}
}
return result;
}
cmDebuggerPipeClient_WIN32::cmDebuggerPipeClient_WIN32(std::string name)
: PipeName(std::move(name))
{
}
cmDebuggerPipeClient_WIN32::~cmDebuggerPipeClient_WIN32()
{
close();
}
void cmDebuggerPipeClient_WIN32::WaitForConnection()
{
if (!isOpen()) {
auto hPipe = CreateFileA(PipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
auto err = GetLastError();
throw std::runtime_error("CreateFile failed with " + err);
}
pipes = std::make_unique<DuplexPipe_WIN32>(hPipe);
}
}
bool cmDebuggerPipeClient_WIN32::isOpen()
{
return pipes != nullptr;
}
void cmDebuggerPipeClient_WIN32::close()
{
if (isOpen()) {
pipes->close();
pipes = nullptr;
}
}
size_t cmDebuggerPipeClient_WIN32::read(void* buffer, size_t n)
{
size_t result = 0;
if (isOpen()) {
result = pipes->read(buffer, n);
if (result == 0) {
close();
}
}
return result;
}
bool cmDebuggerPipeClient_WIN32::write(void const* buffer, size_t n)
{
bool result = false;
if (isOpen()) {
result = pipes->write(buffer, n);
if (!result) {
close();
}
}
return result;
}
#endif // _WIN32
} // namespace cmDebugger
......@@ -12,90 +12,44 @@
#include <string>
#include <thread>
#include <windows.h>
#include <cm3p/cppdap/io.h>
#include <cm3p/uv.h>
#include "cmDebuggerAdapter.h"
#include "cmUVHandlePtr.h"
namespace cmDebugger {
class cmDebuggerPipeBase : public dap::ReaderWriter
#ifdef _WIN32
class DuplexPipe_WIN32
{
public:
cmDebuggerPipeBase(std::string name);
DuplexPipe_WIN32(HANDLE read);
~DuplexPipe_WIN32();
void WaitForConnection();
void close();
size_t read(void* buffer, size_t n);
bool write(void const* buffer, size_t n);
// 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;
bool WaitForConnection();
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;
HANDLE hPipe;
OVERLAPPED readOp;
OVERLAPPED writeOp;
};
class cmDebuggerPipeConnection
: public cmDebuggerPipeBase
class cmDebuggerPipeConnection_WIN32
: public dap::ReaderWriter
, public cmDebuggerConnection
, public std::enable_shared_from_this<cmDebuggerPipeConnection>
, public std::enable_shared_from_this<cmDebuggerPipeConnection_WIN32>
{
public:
cmDebuggerPipeConnection(std::string name);
~cmDebuggerPipeConnection() override;
cmDebuggerPipeConnection_WIN32(std::string name);
~cmDebuggerPipeConnection_WIN32() override;
void WaitForConnection() override
{
cmDebuggerPipeBase::WaitForConnection();
}
void WaitForConnection() override;
bool StartListening(std::string& errorMessage) override;
std::shared_ptr<dap::Reader> GetReader() override;
......@@ -104,36 +58,44 @@ public:
// dap::ReaderWriter implementation
bool isOpen() override;
void close() override;
size_t read(void* buffer, size_t n) override;
bool write(void const* buffer, size_t n) override;
// Used for unit test synchronization
std::promise<void> StartedListening;
private:
void CloseConnection() override;
void Connect(uv_stream_t* server);
void CloseConnection();
std::string GetErrorMessage(DWORD errorCode);
cm::uv_pipe_ptr ServerPipe;
cm::uv_async_ptr ServerPipeClose;
std::string const PipeName;
std::unique_ptr<DuplexPipe_WIN32> pipes;
};
class cmDebuggerPipeClient : public cmDebuggerPipeBase
using cmDebuggerPipeConnection = cmDebuggerPipeConnection_WIN32;
class cmDebuggerPipeClient_WIN32
: public dap::ReaderWriter
, public std::enable_shared_from_this<cmDebuggerPipeClient_WIN32>
{
public:
using cmDebuggerPipeBase::cmDebuggerPipeBase;
~cmDebuggerPipeClient() override;
void Start();
// dap::ReaderWriter implementation
cmDebuggerPipeClient_WIN32(std::string name);
~cmDebuggerPipeClient_WIN32();
void WaitForConnection();
bool isOpen() override;
void close() override;
size_t read(void* buffer, size_t n) override;
bool write(void const* buffer, size_t n) override;
private:
void CloseConnection() override;
void Connect();
void FailConnection();
bool IsConnected = false;
std::string const PipeName;
std::unique_ptr<DuplexPipe_WIN32> pipes;
};
using cmDebuggerPipeClient = cmDebuggerPipeClient_WIN32;
#endif // _WIN32
} // namespace cmDebugger
......@@ -37,7 +37,11 @@
#include "cmCommands.h"
#ifdef CMake_ENABLE_DEBUGGER
# include "cmDebuggerAdapter.h"
# include "cmDebuggerPipeConnection.h"
# ifdef _WIN32
# include "cmDebuggerWindowsPipeConnection.h"
# else //!_WIN32
# include "cmDebuggerPosixPipeConnection.h"
# endif //_WIN32
#endif
#include "cmDocumentation.h"
#include "cmDocumentationEntry.h"
......
......@@ -19,13 +19,15 @@
#include <cm3p/cppdap/types.h>
#include "cmDebuggerAdapter.h"
#include "cmDebuggerPipeConnection.h"
#include "cmDebuggerProtocol.h"
#include "cmVersionConfig.h"
#ifdef _WIN32
# include "cmCryptoHash.h"
# include "cmDebuggerWindowsPipeConnection.h"
# include "cmSystemTools.h"
#else
# include "cmDebuggerPosixPipeConnection.h"
#endif
#include "testCommon.h"
......@@ -128,7 +130,7 @@ bool testProtocolWithPipes()
auto client2Debugger =
std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe);
client2Debugger->Start();
client2Debugger->WaitForConnection();
client->bind(client2Debugger, client2Debugger);
......
......@@ -16,7 +16,12 @@
#include "cmsys/RegularExpression.hxx"
#include "cmDebuggerPipeConnection.h"
#ifdef _WIN32
# include "cmDebuggerWindowsPipeConnection.h"
#else
# include "cmDebuggerPosixPipeConnection.h"
#endif
#include "cmSystemTools.h"
#ifdef _WIN32
......@@ -104,7 +109,7 @@ int runTest(int argc, char* argv[])
attempt++;
try {
client = std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe);
client->Start();
client->WaitForConnection();
std::cout << "cmDebuggerPipeClient connected.\n";
break;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册