Repository: bitcoin/bitcoin
File: src/ipc/capnp/protocol.cpp (C++)

Code before:
#include <cassert>
#include <cerrno>
#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <sys/socket.h>
#include <system_error>
#include <thread>

namespace ipc {
namespace capnp {
namespace {
void IpcLogFn(bool raise, std::string message)
{
    LogDebug(BCLog::IPC, "%s\n", message);
    if (raise) throw Exception(message);
}

class CapnpProtocol : public Protocol
{
public:
    ~CapnpProtocol() noexcept(true)
    {
        m_loop_ref.reset();
        if (m_loop_thread.joinable()) m_loop_thread.join();
        assert(!m_loop);
    };
    std::unique_ptr<interfaces::Init> connect(int fd, const char* exe_name) override
    {
        startLoop(exe_name);
        return mp::ConnectStream<messages::Init>(*m_loop, fd);
    }
    void listen(int listen_fd, const char* exe_name, interfaces::Init& init) override
    {
        startLoop(exe_name);
        if (::listen(listen_fd, /*backlog=*/5) != 0) {
            throw std::system_error(errno, std::system_category());
        }
        mp::ListenConnections<messages::Init>(*m_loop, listen_fd, init);
    }
    void serve(int fd, const char* exe_name, interfaces::Init& init, const std::function<void()>& ready_fn = {}) override
    {
        assert(!m_loop);
        mp::g_thread_context.thread_name = mp::ThreadName(exe_name);
        m_loop.emplace(exe_name, &IpcLogFn, &m_context);
        if (ready_fn) ready_fn();
        mp::ServeStream<messages::Init>(*m_loop, fd, init);
        m_parent_connection = &m_loop->m_incoming_connections.back();
        m_loop->loop();

Review comment (bug):
I'm torn about what to do here, so feedback welcome. Adding another value upstream at any point would cause a crash here. But because we don't know what that future value would be, we can't predict how we should react to it now.

Just commenting because this effectively freezes `mp::Log`, and would require adding new fields to `LogMessage` instead. I'm ok with that, but @ryanofsky might not be.

Code after:
#include <cassert>
#include <cerrno>
#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <sys/socket.h>
#include <system_error>
#include <thread>

namespace ipc {
namespace capnp {
namespace {

BCLog::Level ConvertIPCLogLevel(mp::Log level)
{
    switch (level) {
        case mp::Log::Trace: return BCLog::Level::Trace;
        case mp::Log::Debug: return BCLog::Level::Debug;
        case mp::Log::Info: return BCLog::Level::Info;
        case mp::Log::Warning: return BCLog::Level::Warning;
        case mp::Log::Error: return BCLog::Level::Error;
        case mp::Log::Raise: return BCLog::Level::Error;
    } // no default case, so the compiler can warn about missing cases

    // Be conservative and assume that if MP ever adds a new log level, it
    // should only be shown at our most verbose level.
    return BCLog::Level::Trace;
}

mp::Log GetRequestedIPCLogLevel()
{
    if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Trace)) return mp::Log::Trace;
    if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Debug)) return mp::Log::Debug;

    // Info, Warning, and Error are logged unconditionally
    return mp::Log::Info;
}

void IpcLogFn(mp::LogMessage message)
{
    LogPrintLevel(BCLog::IPC, ConvertIPCLogLevel(message.level), "%s\n", message.message);
    if (message.level == mp::Log::Raise) throw Exception(message.message);
}

class CapnpProtocol : public Protocol
{
public:
    ~CapnpProtocol() noexcept(true)
    {