Coverage Report

Created: 2026-09-02 14:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/ipc/libmultiprocess/include/mp/type-context.h
Line
Count
Source
1
// Copyright (c) The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#ifndef MP_PROXY_TYPE_CONTEXT_H
6
#define MP_PROXY_TYPE_CONTEXT_H
7
8
#include <mp/proxy-io.h>
9
#include <mp/util.h>
10
11
#include <kj/string.h>
12
13
namespace mp {
14
template <typename Output>
15
void CustomBuildField(TypeList<>,
16
    Priority<1>,
17
    ClientInvokeContext& invoke_context,
18
    Output&& output,
19
    typename std::enable_if<std::is_same<decltype(output.get()), Context::Builder>::value>::type* enable = nullptr)
20
26
{
21
26
    auto& connection = invoke_context.connection;
22
26
    auto& thread_context = invoke_context.thread_context;
23
24
    // Create local Thread::Server object corresponding to the current thread
25
    // and pass a Thread::Client reference to it in the Context.callbackThread
26
    // field so the function being called can make callbacks to this thread.
27
    // Also store the Thread::Client reference in the callback_threads map so
28
    // future calls over this connection can reuse it.
29
26
    auto [callback_thread, _]{SetThread(
30
26
        GuardedRef{thread_context.waiter->m_mutex, thread_context.callback_threads}, &connection,
31
26
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Line
Count
Source
31
6
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Line
Count
Source
31
4
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::rpc_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::rpc_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda'()::operator()() const
32
33
    // Call remote ThreadMap.makeThread function so server will create a
34
    // dedicated worker thread to run function calls from this thread. Store the
35
    // Thread::Client reference it returns in the request_threads map.
36
26
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
10
        auto request = connection.m_thread_map.makeThreadRequest();
44
10
        request.setName(thread_context.thread_name);
45
10
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
10
    }};
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Line
Count
Source
36
6
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
6
        auto request = connection.m_thread_map.makeThreadRequest();
44
6
        request.setName(thread_context.thread_name);
45
6
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
6
    }};
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Line
Count
Source
36
4
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
4
        auto request = connection.m_thread_map.makeThreadRequest();
44
4
        request.setName(thread_context.thread_name);
45
4
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
4
    }};
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::rpc_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::rpc_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)::'lambda0'()::operator()() const
47
26
    auto [request_thread, _1]{SetThread(
48
26
        GuardedRef{thread_context.waiter->m_mutex, thread_context.request_threads},
49
26
        &connection, make_request_thread)};
50
51
26
    auto context = output.init();
52
26
    context.setThread(request_thread->second->m_client);
53
26
    context.setCallbackThread(callback_thread->second->m_client);
54
26
}
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Line
Count
Source
20
6
{
21
6
    auto& connection = invoke_context.connection;
22
6
    auto& thread_context = invoke_context.thread_context;
23
24
    // Create local Thread::Server object corresponding to the current thread
25
    // and pass a Thread::Client reference to it in the Context.callbackThread
26
    // field so the function being called can make callbacks to this thread.
27
    // Also store the Thread::Client reference in the callback_threads map so
28
    // future calls over this connection can reuse it.
29
6
    auto [callback_thread, _]{SetThread(
30
6
        GuardedRef{thread_context.waiter->m_mutex, thread_context.callback_threads}, &connection,
31
6
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
32
33
    // Call remote ThreadMap.makeThread function so server will create a
34
    // dedicated worker thread to run function calls from this thread. Store the
35
    // Thread::Client reference it returns in the request_threads map.
36
6
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
6
        auto request = connection.m_thread_map.makeThreadRequest();
44
6
        request.setName(thread_context.thread_name);
45
6
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
6
    }};
47
6
    auto [request_thread, _1]{SetThread(
48
6
        GuardedRef{thread_context.waiter->m_mutex, thread_context.request_threads},
49
6
        &connection, make_request_thread)};
50
51
6
    auto context = output.init();
52
6
    context.setThread(request_thread->second->m_client);
53
6
    context.setCallbackThread(callback_thread->second->m_client);
54
6
}
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::init_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Line
Count
Source
20
4
{
21
4
    auto& connection = invoke_context.connection;
22
4
    auto& thread_context = invoke_context.thread_context;
23
24
    // Create local Thread::Server object corresponding to the current thread
25
    // and pass a Thread::Client reference to it in the Context.callbackThread
26
    // field so the function being called can make callbacks to this thread.
27
    // Also store the Thread::Client reference in the callback_threads map so
28
    // future calls over this connection can reuse it.
29
4
    auto [callback_thread, _]{SetThread(
30
4
        GuardedRef{thread_context.waiter->m_mutex, thread_context.callback_threads}, &connection,
31
4
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
32
33
    // Call remote ThreadMap.makeThread function so server will create a
34
    // dedicated worker thread to run function calls from this thread. Store the
35
    // Thread::Client reference it returns in the request_threads map.
36
4
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
4
        auto request = connection.m_thread_map.makeThreadRequest();
44
4
        request.setName(thread_context.thread_name);
45
4
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
4
    }};
47
4
    auto [request_thread, _1]{SetThread(
48
4
        GuardedRef{thread_context.waiter->m_mutex, thread_context.request_threads},
49
4
        &connection, make_request_thread)};
50
51
4
    auto context = output.init();
52
4
    context.setThread(request_thread->second->m_client);
53
4
    context.setCallbackThread(callback_thread->second->m_client);
54
4
}
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Line
Count
Source
20
6
{
21
6
    auto& connection = invoke_context.connection;
22
6
    auto& thread_context = invoke_context.thread_context;
23
24
    // Create local Thread::Server object corresponding to the current thread
25
    // and pass a Thread::Client reference to it in the Context.callbackThread
26
    // field so the function being called can make callbacks to this thread.
27
    // Also store the Thread::Client reference in the callback_threads map so
28
    // future calls over this connection can reuse it.
29
6
    auto [callback_thread, _]{SetThread(
30
6
        GuardedRef{thread_context.waiter->m_mutex, thread_context.callback_threads}, &connection,
31
6
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
32
33
    // Call remote ThreadMap.makeThread function so server will create a
34
    // dedicated worker thread to run function calls from this thread. Store the
35
    // Thread::Client reference it returns in the request_threads map.
36
6
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
6
        auto request = connection.m_thread_map.makeThreadRequest();
44
6
        request.setName(thread_context.thread_name);
45
6
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
6
    }};
47
6
    auto [request_thread, _1]{SetThread(
48
6
        GuardedRef{thread_context.waiter->m_mutex, thread_context.request_threads},
49
6
        &connection, make_request_thread)};
50
51
6
    auto context = output.init();
52
6
    context.setThread(request_thread->second->m_client);
53
6
    context.setCallbackThread(callback_thread->second->m_client);
54
6
}
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::echo_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Line
Count
Source
20
6
{
21
6
    auto& connection = invoke_context.connection;
22
6
    auto& thread_context = invoke_context.thread_context;
23
24
    // Create local Thread::Server object corresponding to the current thread
25
    // and pass a Thread::Client reference to it in the Context.callbackThread
26
    // field so the function being called can make callbacks to this thread.
27
    // Also store the Thread::Client reference in the callback_threads map so
28
    // future calls over this connection can reuse it.
29
6
    auto [callback_thread, _]{SetThread(
30
6
        GuardedRef{thread_context.waiter->m_mutex, thread_context.callback_threads}, &connection,
31
6
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
32
33
    // Call remote ThreadMap.makeThread function so server will create a
34
    // dedicated worker thread to run function calls from this thread. Store the
35
    // Thread::Client reference it returns in the request_threads map.
36
6
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
6
        auto request = connection.m_thread_map.makeThreadRequest();
44
6
        request.setName(thread_context.thread_name);
45
6
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
6
    }};
47
6
    auto [request_thread, _1]{SetThread(
48
6
        GuardedRef{thread_context.waiter->m_mutex, thread_context.request_threads},
49
6
        &connection, make_request_thread)};
50
51
6
    auto context = output.init();
52
6
    context.setThread(request_thread->second->m_client);
53
6
    context.setCallbackThread(callback_thread->second->m_client);
54
6
}
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Unexecuted instantiation: void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Context, 17>, capnp::Request<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
void mp::CustomBuildField<mp::StructField<mp::Accessor<mp::rpc_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>>(mp::TypeList<>, mp::Priority<1>, mp::ClientInvokeContext&, mp::StructField<mp::Accessor<mp::rpc_fields::Context, 17>, capnp::Request<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&&, std::enable_if<std::is_same<decltype(fp2.get()), mp::Context::Builder>::value, void>::type*)
Line
Count
Source
20
4
{
21
4
    auto& connection = invoke_context.connection;
22
4
    auto& thread_context = invoke_context.thread_context;
23
24
    // Create local Thread::Server object corresponding to the current thread
25
    // and pass a Thread::Client reference to it in the Context.callbackThread
26
    // field so the function being called can make callbacks to this thread.
27
    // Also store the Thread::Client reference in the callback_threads map so
28
    // future calls over this connection can reuse it.
29
4
    auto [callback_thread, _]{SetThread(
30
4
        GuardedRef{thread_context.waiter->m_mutex, thread_context.callback_threads}, &connection,
31
4
        [&] { return connection.m_threads.add(kj::heap<ProxyServer<Thread>>(connection, thread_context, std::thread{})); })};
32
33
    // Call remote ThreadMap.makeThread function so server will create a
34
    // dedicated worker thread to run function calls from this thread. Store the
35
    // Thread::Client reference it returns in the request_threads map.
36
4
    auto make_request_thread{[&]{
37
        // This code will only run if an IPC client call is being made for the
38
        // first time on this thread. After the first call, subsequent calls
39
        // will use the existing request thread. This code will also never run at
40
        // all if the current thread is a request thread created for a different
41
        // IPC client, because in that case PassField code (below) will have set
42
        // request_thread to point to the calling thread.
43
4
        auto request = connection.m_thread_map.makeThreadRequest();
44
4
        request.setName(thread_context.thread_name);
45
4
        return request.send().getResult(); // Nonblocking due to capnp request pipelining.
46
4
    }};
47
4
    auto [request_thread, _1]{SetThread(
48
4
        GuardedRef{thread_context.waiter->m_mutex, thread_context.request_threads},
49
4
        &connection, make_request_thread)};
50
51
4
    auto context = output.init();
52
4
    context.setThread(request_thread->second->m_client);
53
4
    context.setCallbackThread(callback_thread->second->m_client);
54
4
}
55
56
//! PassField override for mp.Context arguments. Return asynchronously and call
57
//! function on other thread found in context.
58
template <typename Accessor, typename ServerContext, typename Fn, typename... Args>
59
auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args) ->
60
    typename std::enable_if<
61
        std::is_same<decltype(Accessor::get(server_context.call_context.getParams())), Context::Reader>::value,
62
        kj::Promise<typename ServerContext::CallContext>>::type
63
246
{
64
246
    auto& server = server_context.proxy_server;
65
246
    EventLoop& loop = *server.m_context.loop;
66
246
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
246
    auto self = server.thisCap();
74
246
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
246
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
246
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
246
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
246
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
246
        auto& thread_context = g_thread_context;
97
246
        auto& request_threads = thread_context.request_threads;
98
246
        ConnThread request_thread;
99
246
        bool inserted{false};
100
246
        Mutex cancel_mutex;
101
246
        Lock cancel_lock{cancel_mutex};
102
246
        server_context.cancel_lock = &cancel_lock;
103
246
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
246
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
246
            assert(!cancel_monitor.m_on_cancel);
111
246
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Line
Count
Source
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda'()::operator()() const
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
246
            std::tie(request_thread, inserted) = SetThread(
132
246
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
246
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
7
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
15
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
37
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
12
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
39
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
36
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
23
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
13
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
24
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
6
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const::'lambda0'()::operator()() const
Line
Count
Source
133
9
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
246
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
7
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
7
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
7
            assert(!cancel_monitor.m_on_cancel);
111
7
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
7
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
7
                Lock cancel_lock{cancel_mutex};
125
7
                server_context.request_canceled = true;
126
7
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
7
            std::tie(request_thread, inserted) = SetThread(
132
7
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
7
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
7
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
15
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
15
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
15
            assert(!cancel_monitor.m_on_cancel);
111
15
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
15
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
15
                Lock cancel_lock{cancel_mutex};
125
15
                server_context.request_canceled = true;
126
15
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
15
            std::tie(request_thread, inserted) = SetThread(
132
15
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
15
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
15
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
4
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
4
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
4
            assert(!cancel_monitor.m_on_cancel);
111
4
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
4
                Lock cancel_lock{cancel_mutex};
125
4
                server_context.request_canceled = true;
126
4
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
4
            std::tie(request_thread, inserted) = SetThread(
132
4
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
4
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
3
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
3
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
3
            assert(!cancel_monitor.m_on_cancel);
111
3
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
3
                Lock cancel_lock{cancel_mutex};
125
3
                server_context.request_canceled = true;
126
3
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
3
            std::tie(request_thread, inserted) = SetThread(
132
3
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
3
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
37
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
37
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
37
            assert(!cancel_monitor.m_on_cancel);
111
37
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
37
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
37
                Lock cancel_lock{cancel_mutex};
125
37
                server_context.request_canceled = true;
126
37
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
37
            std::tie(request_thread, inserted) = SetThread(
132
37
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
37
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
37
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
3
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
3
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
3
            assert(!cancel_monitor.m_on_cancel);
111
3
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
3
                Lock cancel_lock{cancel_mutex};
125
3
                server_context.request_canceled = true;
126
3
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
3
            std::tie(request_thread, inserted) = SetThread(
132
3
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
3
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
12
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
12
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
12
            assert(!cancel_monitor.m_on_cancel);
111
12
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
12
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
12
                Lock cancel_lock{cancel_mutex};
125
12
                server_context.request_canceled = true;
126
12
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
12
            std::tie(request_thread, inserted) = SetThread(
132
12
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
12
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
12
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
39
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
39
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
39
            assert(!cancel_monitor.m_on_cancel);
111
39
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
39
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
39
                Lock cancel_lock{cancel_mutex};
125
39
                server_context.request_canceled = true;
126
39
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
39
            std::tie(request_thread, inserted) = SetThread(
132
39
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
39
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
39
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
36
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
36
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
36
            assert(!cancel_monitor.m_on_cancel);
111
36
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
36
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
36
                Lock cancel_lock{cancel_mutex};
125
36
                server_context.request_canceled = true;
126
36
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
36
            std::tie(request_thread, inserted) = SetThread(
132
36
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
36
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
36
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
23
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
23
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
23
            assert(!cancel_monitor.m_on_cancel);
111
23
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
23
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
23
                Lock cancel_lock{cancel_mutex};
125
23
                server_context.request_canceled = true;
126
23
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
23
            std::tie(request_thread, inserted) = SetThread(
132
23
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
23
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
23
        });
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
13
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
13
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
13
            assert(!cancel_monitor.m_on_cancel);
111
13
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
13
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
13
                Lock cancel_lock{cancel_mutex};
125
13
                server_context.request_canceled = true;
126
13
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
13
            std::tie(request_thread, inserted) = SetThread(
132
13
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
13
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
13
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
24
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
24
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
24
            assert(!cancel_monitor.m_on_cancel);
111
24
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
24
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
24
                Lock cancel_lock{cancel_mutex};
125
24
                server_context.request_canceled = true;
126
24
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
24
            std::tie(request_thread, inserted) = SetThread(
132
24
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
24
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
24
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
4
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
4
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
4
            assert(!cancel_monitor.m_on_cancel);
111
4
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
4
                Lock cancel_lock{cancel_mutex};
125
4
                server_context.request_canceled = true;
126
4
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
4
            std::tie(request_thread, inserted) = SetThread(
132
4
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
4
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
6
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
6
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
6
            assert(!cancel_monitor.m_on_cancel);
111
6
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
6
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
6
                Lock cancel_lock{cancel_mutex};
125
6
                server_context.request_canceled = true;
126
6
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
6
            std::tie(request_thread, inserted) = SetThread(
132
6
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
6
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
6
        });
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda0'()::operator()() const
Line
Count
Source
103
9
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
9
            if (cancel_monitor.m_canceled) {
106
0
                server_context.request_canceled = true;
107
0
                return;
108
0
            }
109
            // Detect request being canceled while it executes.
110
9
            assert(!cancel_monitor.m_on_cancel);
111
9
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
9
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
9
                Lock cancel_lock{cancel_mutex};
125
9
                server_context.request_canceled = true;
126
9
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
9
            std::tie(request_thread, inserted) = SetThread(
132
9
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
9
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
9
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
246
        const bool erase_thread{inserted};
142
246
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
246
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
246
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
246
                cancel_monitor.m_on_cancel = nullptr;
166
246
                auto self_dispose{kj::mv(self)};
167
246
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
246
                    ConnThreads::node_type removed;
175
246
                    {
176
246
                        Lock lock(thread_context.waiter->m_mutex);
177
246
                        removed = request_threads.extract(server.m_context.connection);
178
246
                    }
179
246
                }
180
246
            });
181
246
        );
182
246
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
246
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
246
            try {
186
246
                fn.invoke(server_context, args...);
187
246
            } catch (const InterruptException& e) {
188
246
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
246
            }
190
246
        })) {
191
6
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
6
            kj::throwRecoverableException(kj::mv(*exception));
193
6
        }
194
246
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
246
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
7
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
7
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
7
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
7
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
7
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
7
        auto& thread_context = g_thread_context;
97
7
        auto& request_threads = thread_context.request_threads;
98
7
        ConnThread request_thread;
99
7
        bool inserted{false};
100
7
        Mutex cancel_mutex;
101
7
        Lock cancel_lock{cancel_mutex};
102
7
        server_context.cancel_lock = &cancel_lock;
103
7
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
7
            if (cancel_monitor.m_canceled) {
106
7
                server_context.request_canceled = true;
107
7
                return;
108
7
            }
109
            // Detect request being canceled while it executes.
110
7
            assert(!cancel_monitor.m_on_cancel);
111
7
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
7
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
7
                Lock cancel_lock{cancel_mutex};
125
7
                server_context.request_canceled = true;
126
7
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
7
            std::tie(request_thread, inserted) = SetThread(
132
7
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
7
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
7
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
7
        const bool erase_thread{inserted};
142
7
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
7
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
7
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
7
                cancel_monitor.m_on_cancel = nullptr;
166
7
                auto self_dispose{kj::mv(self)};
167
7
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
7
                    ConnThreads::node_type removed;
175
7
                    {
176
7
                        Lock lock(thread_context.waiter->m_mutex);
177
7
                        removed = request_threads.extract(server.m_context.connection);
178
7
                    }
179
7
                }
180
7
            });
181
7
        );
182
7
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
7
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
7
            try {
186
7
                fn.invoke(server_context, args...);
187
7
            } catch (const InterruptException& e) {
188
7
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
7
            }
190
7
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
7
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
7
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
15
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
15
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
15
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
15
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
15
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
15
        auto& thread_context = g_thread_context;
97
15
        auto& request_threads = thread_context.request_threads;
98
15
        ConnThread request_thread;
99
15
        bool inserted{false};
100
15
        Mutex cancel_mutex;
101
15
        Lock cancel_lock{cancel_mutex};
102
15
        server_context.cancel_lock = &cancel_lock;
103
15
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
15
            if (cancel_monitor.m_canceled) {
106
15
                server_context.request_canceled = true;
107
15
                return;
108
15
            }
109
            // Detect request being canceled while it executes.
110
15
            assert(!cancel_monitor.m_on_cancel);
111
15
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
15
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
15
                Lock cancel_lock{cancel_mutex};
125
15
                server_context.request_canceled = true;
126
15
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
15
            std::tie(request_thread, inserted) = SetThread(
132
15
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
15
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
15
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
15
        const bool erase_thread{inserted};
142
15
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
15
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
15
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
15
                cancel_monitor.m_on_cancel = nullptr;
166
15
                auto self_dispose{kj::mv(self)};
167
15
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
15
                    ConnThreads::node_type removed;
175
15
                    {
176
15
                        Lock lock(thread_context.waiter->m_mutex);
177
15
                        removed = request_threads.extract(server.m_context.connection);
178
15
                    }
179
15
                }
180
15
            });
181
15
        );
182
15
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
15
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
15
            try {
186
15
                fn.invoke(server_context, args...);
187
15
            } catch (const InterruptException& e) {
188
15
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
15
            }
190
15
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
15
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
15
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
4
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
4
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
4
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
4
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
4
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
4
        auto& thread_context = g_thread_context;
97
4
        auto& request_threads = thread_context.request_threads;
98
4
        ConnThread request_thread;
99
4
        bool inserted{false};
100
4
        Mutex cancel_mutex;
101
4
        Lock cancel_lock{cancel_mutex};
102
4
        server_context.cancel_lock = &cancel_lock;
103
4
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
4
            if (cancel_monitor.m_canceled) {
106
4
                server_context.request_canceled = true;
107
4
                return;
108
4
            }
109
            // Detect request being canceled while it executes.
110
4
            assert(!cancel_monitor.m_on_cancel);
111
4
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
4
                Lock cancel_lock{cancel_mutex};
125
4
                server_context.request_canceled = true;
126
4
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
4
            std::tie(request_thread, inserted) = SetThread(
132
4
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
4
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
4
        const bool erase_thread{inserted};
142
4
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
4
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
4
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
4
                cancel_monitor.m_on_cancel = nullptr;
166
4
                auto self_dispose{kj::mv(self)};
167
4
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
4
                    ConnThreads::node_type removed;
175
4
                    {
176
4
                        Lock lock(thread_context.waiter->m_mutex);
177
4
                        removed = request_threads.extract(server.m_context.connection);
178
4
                    }
179
4
                }
180
4
            });
181
4
        );
182
4
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
4
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
4
            try {
186
4
                fn.invoke(server_context, args...);
187
4
            } catch (const InterruptException& e) {
188
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
4
            }
190
4
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
4
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
4
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
2
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
2
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
2
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
2
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
2
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
2
        auto& thread_context = g_thread_context;
97
2
        auto& request_threads = thread_context.request_threads;
98
2
        ConnThread request_thread;
99
2
        bool inserted{false};
100
2
        Mutex cancel_mutex;
101
2
        Lock cancel_lock{cancel_mutex};
102
2
        server_context.cancel_lock = &cancel_lock;
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
2
                server_context.request_canceled = true;
107
2
                return;
108
2
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
2
        const bool erase_thread{inserted};
142
2
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
2
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
2
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
2
                cancel_monitor.m_on_cancel = nullptr;
166
2
                auto self_dispose{kj::mv(self)};
167
2
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
2
                    ConnThreads::node_type removed;
175
2
                    {
176
2
                        Lock lock(thread_context.waiter->m_mutex);
177
2
                        removed = request_threads.extract(server.m_context.connection);
178
2
                    }
179
2
                }
180
2
            });
181
2
        );
182
2
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
2
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
2
            try {
186
2
                fn.invoke(server_context, args...);
187
2
            } catch (const InterruptException& e) {
188
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
2
            }
190
2
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
2
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
2
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
3
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
3
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
3
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
3
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
3
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
3
        auto& thread_context = g_thread_context;
97
3
        auto& request_threads = thread_context.request_threads;
98
3
        ConnThread request_thread;
99
3
        bool inserted{false};
100
3
        Mutex cancel_mutex;
101
3
        Lock cancel_lock{cancel_mutex};
102
3
        server_context.cancel_lock = &cancel_lock;
103
3
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
3
            if (cancel_monitor.m_canceled) {
106
3
                server_context.request_canceled = true;
107
3
                return;
108
3
            }
109
            // Detect request being canceled while it executes.
110
3
            assert(!cancel_monitor.m_on_cancel);
111
3
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
3
                Lock cancel_lock{cancel_mutex};
125
3
                server_context.request_canceled = true;
126
3
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
3
            std::tie(request_thread, inserted) = SetThread(
132
3
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
3
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
3
        const bool erase_thread{inserted};
142
3
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
3
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
3
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
3
                cancel_monitor.m_on_cancel = nullptr;
166
3
                auto self_dispose{kj::mv(self)};
167
3
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
3
                    ConnThreads::node_type removed;
175
3
                    {
176
3
                        Lock lock(thread_context.waiter->m_mutex);
177
3
                        removed = request_threads.extract(server.m_context.connection);
178
3
                    }
179
3
                }
180
3
            });
181
3
        );
182
3
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
3
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
3
            try {
186
3
                fn.invoke(server_context, args...);
187
3
            } catch (const InterruptException& e) {
188
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
3
            }
190
3
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
3
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
3
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
37
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
37
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
37
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
37
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
37
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
37
        auto& thread_context = g_thread_context;
97
37
        auto& request_threads = thread_context.request_threads;
98
37
        ConnThread request_thread;
99
37
        bool inserted{false};
100
37
        Mutex cancel_mutex;
101
37
        Lock cancel_lock{cancel_mutex};
102
37
        server_context.cancel_lock = &cancel_lock;
103
37
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
37
            if (cancel_monitor.m_canceled) {
106
37
                server_context.request_canceled = true;
107
37
                return;
108
37
            }
109
            // Detect request being canceled while it executes.
110
37
            assert(!cancel_monitor.m_on_cancel);
111
37
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
37
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
37
                Lock cancel_lock{cancel_mutex};
125
37
                server_context.request_canceled = true;
126
37
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
37
            std::tie(request_thread, inserted) = SetThread(
132
37
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
37
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
37
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
37
        const bool erase_thread{inserted};
142
37
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
37
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
37
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
37
                cancel_monitor.m_on_cancel = nullptr;
166
37
                auto self_dispose{kj::mv(self)};
167
37
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
37
                    ConnThreads::node_type removed;
175
37
                    {
176
37
                        Lock lock(thread_context.waiter->m_mutex);
177
37
                        removed = request_threads.extract(server.m_context.connection);
178
37
                    }
179
37
                }
180
37
            });
181
37
        );
182
37
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
37
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
37
            try {
186
37
                fn.invoke(server_context, args...);
187
37
            } catch (const InterruptException& e) {
188
37
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
37
            }
190
37
        })) {
191
3
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
3
            kj::throwRecoverableException(kj::mv(*exception));
193
3
        }
194
37
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
37
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
3
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
3
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
3
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
3
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
3
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
3
        auto& thread_context = g_thread_context;
97
3
        auto& request_threads = thread_context.request_threads;
98
3
        ConnThread request_thread;
99
3
        bool inserted{false};
100
3
        Mutex cancel_mutex;
101
3
        Lock cancel_lock{cancel_mutex};
102
3
        server_context.cancel_lock = &cancel_lock;
103
3
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
3
            if (cancel_monitor.m_canceled) {
106
3
                server_context.request_canceled = true;
107
3
                return;
108
3
            }
109
            // Detect request being canceled while it executes.
110
3
            assert(!cancel_monitor.m_on_cancel);
111
3
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
3
                Lock cancel_lock{cancel_mutex};
125
3
                server_context.request_canceled = true;
126
3
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
3
            std::tie(request_thread, inserted) = SetThread(
132
3
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
3
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
3
        const bool erase_thread{inserted};
142
3
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
3
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
3
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
3
                cancel_monitor.m_on_cancel = nullptr;
166
3
                auto self_dispose{kj::mv(self)};
167
3
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
3
                    ConnThreads::node_type removed;
175
3
                    {
176
3
                        Lock lock(thread_context.waiter->m_mutex);
177
3
                        removed = request_threads.extract(server.m_context.connection);
178
3
                    }
179
3
                }
180
3
            });
181
3
        );
182
3
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
3
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
3
            try {
186
3
                fn.invoke(server_context, args...);
187
3
            } catch (const InterruptException& e) {
188
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
3
            }
190
3
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
3
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
3
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
12
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
12
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
12
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
12
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
12
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
12
        auto& thread_context = g_thread_context;
97
12
        auto& request_threads = thread_context.request_threads;
98
12
        ConnThread request_thread;
99
12
        bool inserted{false};
100
12
        Mutex cancel_mutex;
101
12
        Lock cancel_lock{cancel_mutex};
102
12
        server_context.cancel_lock = &cancel_lock;
103
12
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
12
            if (cancel_monitor.m_canceled) {
106
12
                server_context.request_canceled = true;
107
12
                return;
108
12
            }
109
            // Detect request being canceled while it executes.
110
12
            assert(!cancel_monitor.m_on_cancel);
111
12
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
12
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
12
                Lock cancel_lock{cancel_mutex};
125
12
                server_context.request_canceled = true;
126
12
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
12
            std::tie(request_thread, inserted) = SetThread(
132
12
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
12
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
12
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
12
        const bool erase_thread{inserted};
142
12
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
12
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
12
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
12
                cancel_monitor.m_on_cancel = nullptr;
166
12
                auto self_dispose{kj::mv(self)};
167
12
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
12
                    ConnThreads::node_type removed;
175
12
                    {
176
12
                        Lock lock(thread_context.waiter->m_mutex);
177
12
                        removed = request_threads.extract(server.m_context.connection);
178
12
                    }
179
12
                }
180
12
            });
181
12
        );
182
12
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
12
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
12
            try {
186
12
                fn.invoke(server_context, args...);
187
12
            } catch (const InterruptException& e) {
188
12
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
12
            }
190
12
        })) {
191
2
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
2
            kj::throwRecoverableException(kj::mv(*exception));
193
2
        }
194
12
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
12
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
2
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
2
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
2
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
2
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
2
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
2
        auto& thread_context = g_thread_context;
97
2
        auto& request_threads = thread_context.request_threads;
98
2
        ConnThread request_thread;
99
2
        bool inserted{false};
100
2
        Mutex cancel_mutex;
101
2
        Lock cancel_lock{cancel_mutex};
102
2
        server_context.cancel_lock = &cancel_lock;
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
2
                server_context.request_canceled = true;
107
2
                return;
108
2
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
2
        const bool erase_thread{inserted};
142
2
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
2
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
2
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
2
                cancel_monitor.m_on_cancel = nullptr;
166
2
                auto self_dispose{kj::mv(self)};
167
2
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
2
                    ConnThreads::node_type removed;
175
2
                    {
176
2
                        Lock lock(thread_context.waiter->m_mutex);
177
2
                        removed = request_threads.extract(server.m_context.connection);
178
2
                    }
179
2
                }
180
2
            });
181
2
        );
182
2
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
2
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
2
            try {
186
2
                fn.invoke(server_context, args...);
187
2
            } catch (const InterruptException& e) {
188
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
2
            }
190
2
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
2
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
2
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
2
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
2
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
2
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
2
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
2
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
2
        auto& thread_context = g_thread_context;
97
2
        auto& request_threads = thread_context.request_threads;
98
2
        ConnThread request_thread;
99
2
        bool inserted{false};
100
2
        Mutex cancel_mutex;
101
2
        Lock cancel_lock{cancel_mutex};
102
2
        server_context.cancel_lock = &cancel_lock;
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
2
                server_context.request_canceled = true;
107
2
                return;
108
2
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
2
        const bool erase_thread{inserted};
142
2
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
2
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
2
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
2
                cancel_monitor.m_on_cancel = nullptr;
166
2
                auto self_dispose{kj::mv(self)};
167
2
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
2
                    ConnThreads::node_type removed;
175
2
                    {
176
2
                        Lock lock(thread_context.waiter->m_mutex);
177
2
                        removed = request_threads.extract(server.m_context.connection);
178
2
                    }
179
2
                }
180
2
            });
181
2
        );
182
2
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
2
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
2
            try {
186
2
                fn.invoke(server_context, args...);
187
2
            } catch (const InterruptException& e) {
188
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
2
            }
190
2
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
2
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
2
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
39
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
39
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
39
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
39
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
39
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
39
        auto& thread_context = g_thread_context;
97
39
        auto& request_threads = thread_context.request_threads;
98
39
        ConnThread request_thread;
99
39
        bool inserted{false};
100
39
        Mutex cancel_mutex;
101
39
        Lock cancel_lock{cancel_mutex};
102
39
        server_context.cancel_lock = &cancel_lock;
103
39
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
39
            if (cancel_monitor.m_canceled) {
106
39
                server_context.request_canceled = true;
107
39
                return;
108
39
            }
109
            // Detect request being canceled while it executes.
110
39
            assert(!cancel_monitor.m_on_cancel);
111
39
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
39
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
39
                Lock cancel_lock{cancel_mutex};
125
39
                server_context.request_canceled = true;
126
39
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
39
            std::tie(request_thread, inserted) = SetThread(
132
39
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
39
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
39
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
39
        const bool erase_thread{inserted};
142
39
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
39
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
39
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
39
                cancel_monitor.m_on_cancel = nullptr;
166
39
                auto self_dispose{kj::mv(self)};
167
39
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
39
                    ConnThreads::node_type removed;
175
39
                    {
176
39
                        Lock lock(thread_context.waiter->m_mutex);
177
39
                        removed = request_threads.extract(server.m_context.connection);
178
39
                    }
179
39
                }
180
39
            });
181
39
        );
182
39
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
39
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
39
            try {
186
39
                fn.invoke(server_context, args...);
187
39
            } catch (const InterruptException& e) {
188
39
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
39
            }
190
39
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
39
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
39
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
36
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
36
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
36
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
36
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
36
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
36
        auto& thread_context = g_thread_context;
97
36
        auto& request_threads = thread_context.request_threads;
98
36
        ConnThread request_thread;
99
36
        bool inserted{false};
100
36
        Mutex cancel_mutex;
101
36
        Lock cancel_lock{cancel_mutex};
102
36
        server_context.cancel_lock = &cancel_lock;
103
36
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
36
            if (cancel_monitor.m_canceled) {
106
36
                server_context.request_canceled = true;
107
36
                return;
108
36
            }
109
            // Detect request being canceled while it executes.
110
36
            assert(!cancel_monitor.m_on_cancel);
111
36
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
36
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
36
                Lock cancel_lock{cancel_mutex};
125
36
                server_context.request_canceled = true;
126
36
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
36
            std::tie(request_thread, inserted) = SetThread(
132
36
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
36
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
36
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
36
        const bool erase_thread{inserted};
142
36
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
36
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
36
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
36
                cancel_monitor.m_on_cancel = nullptr;
166
36
                auto self_dispose{kj::mv(self)};
167
36
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
36
                    ConnThreads::node_type removed;
175
36
                    {
176
36
                        Lock lock(thread_context.waiter->m_mutex);
177
36
                        removed = request_threads.extract(server.m_context.connection);
178
36
                    }
179
36
                }
180
36
            });
181
36
        );
182
36
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
36
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
36
            try {
186
36
                fn.invoke(server_context, args...);
187
36
            } catch (const InterruptException& e) {
188
36
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
36
            }
190
36
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
36
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
36
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
23
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
23
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
23
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
23
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
23
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
23
        auto& thread_context = g_thread_context;
97
23
        auto& request_threads = thread_context.request_threads;
98
23
        ConnThread request_thread;
99
23
        bool inserted{false};
100
23
        Mutex cancel_mutex;
101
23
        Lock cancel_lock{cancel_mutex};
102
23
        server_context.cancel_lock = &cancel_lock;
103
23
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
23
            if (cancel_monitor.m_canceled) {
106
23
                server_context.request_canceled = true;
107
23
                return;
108
23
            }
109
            // Detect request being canceled while it executes.
110
23
            assert(!cancel_monitor.m_on_cancel);
111
23
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
23
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
23
                Lock cancel_lock{cancel_mutex};
125
23
                server_context.request_canceled = true;
126
23
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
23
            std::tie(request_thread, inserted) = SetThread(
132
23
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
23
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
23
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
23
        const bool erase_thread{inserted};
142
23
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
23
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
23
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
23
                cancel_monitor.m_on_cancel = nullptr;
166
23
                auto self_dispose{kj::mv(self)};
167
23
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
23
                    ConnThreads::node_type removed;
175
23
                    {
176
23
                        Lock lock(thread_context.waiter->m_mutex);
177
23
                        removed = request_threads.extract(server.m_context.connection);
178
23
                    }
179
23
                }
180
23
            });
181
23
        );
182
23
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
23
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
23
            try {
186
23
                fn.invoke(server_context, args...);
187
23
            } catch (const InterruptException& e) {
188
23
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
23
            }
190
23
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
23
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
23
    };
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
13
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
13
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
13
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
13
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
13
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
13
        auto& thread_context = g_thread_context;
97
13
        auto& request_threads = thread_context.request_threads;
98
13
        ConnThread request_thread;
99
13
        bool inserted{false};
100
13
        Mutex cancel_mutex;
101
13
        Lock cancel_lock{cancel_mutex};
102
13
        server_context.cancel_lock = &cancel_lock;
103
13
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
13
            if (cancel_monitor.m_canceled) {
106
13
                server_context.request_canceled = true;
107
13
                return;
108
13
            }
109
            // Detect request being canceled while it executes.
110
13
            assert(!cancel_monitor.m_on_cancel);
111
13
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
13
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
13
                Lock cancel_lock{cancel_mutex};
125
13
                server_context.request_canceled = true;
126
13
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
13
            std::tie(request_thread, inserted) = SetThread(
132
13
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
13
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
13
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
13
        const bool erase_thread{inserted};
142
13
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
13
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
13
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
13
                cancel_monitor.m_on_cancel = nullptr;
166
13
                auto self_dispose{kj::mv(self)};
167
13
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
13
                    ConnThreads::node_type removed;
175
13
                    {
176
13
                        Lock lock(thread_context.waiter->m_mutex);
177
13
                        removed = request_threads.extract(server.m_context.connection);
178
13
                    }
179
13
                }
180
13
            });
181
13
        );
182
13
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
13
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
13
            try {
186
13
                fn.invoke(server_context, args...);
187
13
            } catch (const InterruptException& e) {
188
13
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
13
            }
190
13
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
13
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
13
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
24
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
24
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
24
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
24
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
24
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
24
        auto& thread_context = g_thread_context;
97
24
        auto& request_threads = thread_context.request_threads;
98
24
        ConnThread request_thread;
99
24
        bool inserted{false};
100
24
        Mutex cancel_mutex;
101
24
        Lock cancel_lock{cancel_mutex};
102
24
        server_context.cancel_lock = &cancel_lock;
103
24
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
24
            if (cancel_monitor.m_canceled) {
106
24
                server_context.request_canceled = true;
107
24
                return;
108
24
            }
109
            // Detect request being canceled while it executes.
110
24
            assert(!cancel_monitor.m_on_cancel);
111
24
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
24
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
24
                Lock cancel_lock{cancel_mutex};
125
24
                server_context.request_canceled = true;
126
24
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
24
            std::tie(request_thread, inserted) = SetThread(
132
24
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
24
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
24
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
24
        const bool erase_thread{inserted};
142
24
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
24
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
24
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
24
                cancel_monitor.m_on_cancel = nullptr;
166
24
                auto self_dispose{kj::mv(self)};
167
24
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
24
                    ConnThreads::node_type removed;
175
24
                    {
176
24
                        Lock lock(thread_context.waiter->m_mutex);
177
24
                        removed = request_threads.extract(server.m_context.connection);
178
24
                    }
179
24
                }
180
24
            });
181
24
        );
182
24
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
24
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
24
            try {
186
24
                fn.invoke(server_context, args...);
187
24
            } catch (const InterruptException& e) {
188
24
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
24
            }
190
24
        })) {
191
1
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
1
            kj::throwRecoverableException(kj::mv(*exception));
193
1
        }
194
24
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
24
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
4
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
4
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
4
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
4
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
4
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
4
        auto& thread_context = g_thread_context;
97
4
        auto& request_threads = thread_context.request_threads;
98
4
        ConnThread request_thread;
99
4
        bool inserted{false};
100
4
        Mutex cancel_mutex;
101
4
        Lock cancel_lock{cancel_mutex};
102
4
        server_context.cancel_lock = &cancel_lock;
103
4
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
4
            if (cancel_monitor.m_canceled) {
106
4
                server_context.request_canceled = true;
107
4
                return;
108
4
            }
109
            // Detect request being canceled while it executes.
110
4
            assert(!cancel_monitor.m_on_cancel);
111
4
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
4
                Lock cancel_lock{cancel_mutex};
125
4
                server_context.request_canceled = true;
126
4
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
4
            std::tie(request_thread, inserted) = SetThread(
132
4
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
4
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
4
        const bool erase_thread{inserted};
142
4
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
4
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
4
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
4
                cancel_monitor.m_on_cancel = nullptr;
166
4
                auto self_dispose{kj::mv(self)};
167
4
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
4
                    ConnThreads::node_type removed;
175
4
                    {
176
4
                        Lock lock(thread_context.waiter->m_mutex);
177
4
                        removed = request_threads.extract(server.m_context.connection);
178
4
                    }
179
4
                }
180
4
            });
181
4
        );
182
4
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
4
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
4
            try {
186
4
                fn.invoke(server_context, args...);
187
4
            } catch (const InterruptException& e) {
188
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
4
            }
190
4
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
4
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
4
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
6
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
6
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
6
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
6
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
6
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
6
        auto& thread_context = g_thread_context;
97
6
        auto& request_threads = thread_context.request_threads;
98
6
        ConnThread request_thread;
99
6
        bool inserted{false};
100
6
        Mutex cancel_mutex;
101
6
        Lock cancel_lock{cancel_mutex};
102
6
        server_context.cancel_lock = &cancel_lock;
103
6
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
6
            if (cancel_monitor.m_canceled) {
106
6
                server_context.request_canceled = true;
107
6
                return;
108
6
            }
109
            // Detect request being canceled while it executes.
110
6
            assert(!cancel_monitor.m_on_cancel);
111
6
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
6
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
6
                Lock cancel_lock{cancel_mutex};
125
6
                server_context.request_canceled = true;
126
6
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
6
            std::tie(request_thread, inserted) = SetThread(
132
6
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
6
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
6
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
6
        const bool erase_thread{inserted};
142
6
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
6
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
6
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
6
                cancel_monitor.m_on_cancel = nullptr;
166
6
                auto self_dispose{kj::mv(self)};
167
6
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
6
                    ConnThreads::node_type removed;
175
6
                    {
176
6
                        Lock lock(thread_context.waiter->m_mutex);
177
6
                        removed = request_threads.extract(server.m_context.connection);
178
6
                    }
179
6
                }
180
6
            });
181
6
        );
182
6
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
6
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
6
            try {
186
6
                fn.invoke(server_context, args...);
187
6
            } catch (const InterruptException& e) {
188
6
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
6
            }
190
6
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
6
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
6
    };
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)
Line
Count
Source
74
9
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
9
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
9
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
9
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
9
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
9
        auto& thread_context = g_thread_context;
97
9
        auto& request_threads = thread_context.request_threads;
98
9
        ConnThread request_thread;
99
9
        bool inserted{false};
100
9
        Mutex cancel_mutex;
101
9
        Lock cancel_lock{cancel_mutex};
102
9
        server_context.cancel_lock = &cancel_lock;
103
9
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
9
            if (cancel_monitor.m_canceled) {
106
9
                server_context.request_canceled = true;
107
9
                return;
108
9
            }
109
            // Detect request being canceled while it executes.
110
9
            assert(!cancel_monitor.m_on_cancel);
111
9
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
9
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
9
                Lock cancel_lock{cancel_mutex};
125
9
                server_context.request_canceled = true;
126
9
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
9
            std::tie(request_thread, inserted) = SetThread(
132
9
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
9
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
9
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
9
        const bool erase_thread{inserted};
142
9
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
9
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
9
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
9
                cancel_monitor.m_on_cancel = nullptr;
166
9
                auto self_dispose{kj::mv(self)};
167
9
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
9
                    ConnThreads::node_type removed;
175
9
                    {
176
9
                        Lock lock(thread_context.waiter->m_mutex);
177
9
                        removed = request_threads.extract(server.m_context.connection);
178
9
                    }
179
9
                }
180
9
            });
181
9
        );
182
9
        if (server_context.request_canceled) {
183
0
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
9
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
9
            try {
186
9
                fn.invoke(server_context, args...);
187
9
            } catch (const InterruptException& e) {
188
9
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
9
            }
190
9
        })) {
191
0
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
0
            kj::throwRecoverableException(kj::mv(*exception));
193
0
        }
194
9
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
9
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
246
    const auto& params = server_context.call_context.getParams();
202
246
    Context::Reader context_arg = Accessor::get(params);
203
246
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'()::operator()() const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda'()::operator()() const
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
246
    } else {
234
246
        auto thread_client = context_arg.getThread();
235
246
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
246
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
246
                KJ_IF_MAYBE (thread_server, perhaps) {
241
246
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
246
                    MP_LOG(loop, Log::Debug)
243
246
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
246
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
246
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
246
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
7
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
7
                KJ_IF_MAYBE (thread_server, perhaps) {
241
7
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
7
                    MP_LOG(loop, Log::Debug)
243
7
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
7
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
7
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
7
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
15
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
15
                KJ_IF_MAYBE (thread_server, perhaps) {
241
15
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
15
                    MP_LOG(loop, Log::Debug)
243
15
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
15
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
15
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
15
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
4
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
4
                KJ_IF_MAYBE (thread_server, perhaps) {
241
4
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
4
                    MP_LOG(loop, Log::Debug)
243
4
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
4
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
4
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
4
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
2
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
2
                KJ_IF_MAYBE (thread_server, perhaps) {
241
2
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
2
                    MP_LOG(loop, Log::Debug)
243
2
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
2
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
2
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
2
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
3
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
3
                KJ_IF_MAYBE (thread_server, perhaps) {
241
3
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
3
                    MP_LOG(loop, Log::Debug)
243
3
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
3
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
3
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
3
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
37
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
37
                KJ_IF_MAYBE (thread_server, perhaps) {
241
37
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
37
                    MP_LOG(loop, Log::Debug)
243
37
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
37
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
37
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
37
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
3
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
3
                KJ_IF_MAYBE (thread_server, perhaps) {
241
3
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
3
                    MP_LOG(loop, Log::Debug)
243
3
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
3
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
3
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
3
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
12
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
12
                KJ_IF_MAYBE (thread_server, perhaps) {
241
12
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
12
                    MP_LOG(loop, Log::Debug)
243
12
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
12
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
12
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
12
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
2
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
2
                KJ_IF_MAYBE (thread_server, perhaps) {
241
2
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
2
                    MP_LOG(loop, Log::Debug)
243
2
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
2
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
2
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
2
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
2
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
2
                KJ_IF_MAYBE (thread_server, perhaps) {
241
2
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
2
                    MP_LOG(loop, Log::Debug)
243
2
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
2
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
2
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
2
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
39
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
39
                KJ_IF_MAYBE (thread_server, perhaps) {
241
39
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
39
                    MP_LOG(loop, Log::Debug)
243
39
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
39
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
39
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
39
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
36
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
36
                KJ_IF_MAYBE (thread_server, perhaps) {
241
36
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
36
                    MP_LOG(loop, Log::Debug)
243
36
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
36
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
36
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
36
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
23
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
23
                KJ_IF_MAYBE (thread_server, perhaps) {
241
23
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
23
                    MP_LOG(loop, Log::Debug)
243
23
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
23
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
23
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
23
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
13
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
13
                KJ_IF_MAYBE (thread_server, perhaps) {
241
13
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
13
                    MP_LOG(loop, Log::Debug)
243
13
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
13
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
13
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
13
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
24
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
24
                KJ_IF_MAYBE (thread_server, perhaps) {
241
24
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
24
                    MP_LOG(loop, Log::Debug)
243
24
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
24
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
24
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
24
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
4
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
4
                KJ_IF_MAYBE (thread_server, perhaps) {
241
4
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
4
                    MP_LOG(loop, Log::Debug)
243
4
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
4
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
4
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
4
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
6
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
6
                KJ_IF_MAYBE (thread_server, perhaps) {
241
6
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
6
                    MP_LOG(loop, Log::Debug)
243
6
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
6
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
6
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
6
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda0'(kj::Maybe<mp::Thread::Server&> const&)::operator()(kj::Maybe<mp::Thread::Server&> const&)
Line
Count
Source
236
9
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
9
                KJ_IF_MAYBE (thread_server, perhaps) {
241
9
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
9
                    MP_LOG(loop, Log::Debug)
243
9
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
9
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
9
                } else {
246
0
                    MP_LOG(loop, Log::Error)
247
0
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
0
                    throw std::runtime_error("invalid thread handle");
249
0
                }
250
9
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
0
                MP_LOG(loop, Log::Info)
282
0
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
0
                return kj::mv(e);
284
0
            });
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)::'lambda'(kj::Exception&&)::operator()(kj::Exception&&) const
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
246
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
246
    }
292
246
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeEchoParams, ipc::capnp::messages::Init::MakeEchoResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
7
{
64
7
    auto& server = server_context.proxy_server;
65
7
    EventLoop& loop = *server.m_context.loop;
66
7
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
7
    auto self = server.thisCap();
74
7
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
7
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
7
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
7
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
7
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
7
        auto& thread_context = g_thread_context;
97
7
        auto& request_threads = thread_context.request_threads;
98
7
        ConnThread request_thread;
99
7
        bool inserted{false};
100
7
        Mutex cancel_mutex;
101
7
        Lock cancel_lock{cancel_mutex};
102
7
        server_context.cancel_lock = &cancel_lock;
103
7
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
7
            if (cancel_monitor.m_canceled) {
106
7
                server_context.request_canceled = true;
107
7
                return;
108
7
            }
109
            // Detect request being canceled while it executes.
110
7
            assert(!cancel_monitor.m_on_cancel);
111
7
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
7
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
7
                Lock cancel_lock{cancel_mutex};
125
7
                server_context.request_canceled = true;
126
7
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
7
            std::tie(request_thread, inserted) = SetThread(
132
7
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
7
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
7
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
7
        const bool erase_thread{inserted};
142
7
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
7
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
7
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
7
                cancel_monitor.m_on_cancel = nullptr;
166
7
                auto self_dispose{kj::mv(self)};
167
7
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
7
                    ConnThreads::node_type removed;
175
7
                    {
176
7
                        Lock lock(thread_context.waiter->m_mutex);
177
7
                        removed = request_threads.extract(server.m_context.connection);
178
7
                    }
179
7
                }
180
7
            });
181
7
        );
182
7
        if (server_context.request_canceled) {
183
7
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
7
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
7
            try {
186
7
                fn.invoke(server_context, args...);
187
7
            } catch (const InterruptException& e) {
188
7
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
7
            }
190
7
        })) {
191
7
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
7
            kj::throwRecoverableException(kj::mv(*exception));
193
7
        }
194
7
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
7
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
7
    const auto& params = server_context.call_context.getParams();
202
7
    Context::Reader context_arg = Accessor::get(params);
203
7
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
7
    } else {
234
7
        auto thread_client = context_arg.getThread();
235
7
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
7
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
7
                KJ_IF_MAYBE (thread_server, perhaps) {
241
7
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
7
                    MP_LOG(loop, Log::Debug)
243
7
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
7
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
7
                } else {
246
7
                    MP_LOG(loop, Log::Error)
247
7
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
7
                    throw std::runtime_error("invalid thread handle");
249
7
                }
250
7
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
7
                MP_LOG(loop, Log::Info)
282
7
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
7
                return kj::mv(e);
284
7
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
7
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
7
    }
292
7
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeMiningParams, ipc::capnp::messages::Init::MakeMiningResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
15
{
64
15
    auto& server = server_context.proxy_server;
65
15
    EventLoop& loop = *server.m_context.loop;
66
15
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
15
    auto self = server.thisCap();
74
15
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
15
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
15
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
15
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
15
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
15
        auto& thread_context = g_thread_context;
97
15
        auto& request_threads = thread_context.request_threads;
98
15
        ConnThread request_thread;
99
15
        bool inserted{false};
100
15
        Mutex cancel_mutex;
101
15
        Lock cancel_lock{cancel_mutex};
102
15
        server_context.cancel_lock = &cancel_lock;
103
15
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
15
            if (cancel_monitor.m_canceled) {
106
15
                server_context.request_canceled = true;
107
15
                return;
108
15
            }
109
            // Detect request being canceled while it executes.
110
15
            assert(!cancel_monitor.m_on_cancel);
111
15
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
15
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
15
                Lock cancel_lock{cancel_mutex};
125
15
                server_context.request_canceled = true;
126
15
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
15
            std::tie(request_thread, inserted) = SetThread(
132
15
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
15
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
15
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
15
        const bool erase_thread{inserted};
142
15
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
15
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
15
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
15
                cancel_monitor.m_on_cancel = nullptr;
166
15
                auto self_dispose{kj::mv(self)};
167
15
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
15
                    ConnThreads::node_type removed;
175
15
                    {
176
15
                        Lock lock(thread_context.waiter->m_mutex);
177
15
                        removed = request_threads.extract(server.m_context.connection);
178
15
                    }
179
15
                }
180
15
            });
181
15
        );
182
15
        if (server_context.request_canceled) {
183
15
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
15
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
15
            try {
186
15
                fn.invoke(server_context, args...);
187
15
            } catch (const InterruptException& e) {
188
15
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
15
            }
190
15
        })) {
191
15
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
15
            kj::throwRecoverableException(kj::mv(*exception));
193
15
        }
194
15
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
15
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
15
    const auto& params = server_context.call_context.getParams();
202
15
    Context::Reader context_arg = Accessor::get(params);
203
15
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
15
    } else {
234
15
        auto thread_client = context_arg.getThread();
235
15
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
15
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
15
                KJ_IF_MAYBE (thread_server, perhaps) {
241
15
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
15
                    MP_LOG(loop, Log::Debug)
243
15
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
15
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
15
                } else {
246
15
                    MP_LOG(loop, Log::Error)
247
15
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
15
                    throw std::runtime_error("invalid thread handle");
249
15
                }
250
15
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
15
                MP_LOG(loop, Log::Info)
282
15
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
15
                return kj::mv(e);
284
15
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
15
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
15
    }
292
15
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::init_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::init_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Init>, capnp::CallContext<ipc::capnp::messages::Init::MakeRpcParams, ipc::capnp::messages::Init::MakeRpcResults>>&, mp::ServerRet<mp::Accessor<mp::init_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
4
{
64
4
    auto& server = server_context.proxy_server;
65
4
    EventLoop& loop = *server.m_context.loop;
66
4
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
4
    auto self = server.thisCap();
74
4
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
4
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
4
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
4
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
4
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
4
        auto& thread_context = g_thread_context;
97
4
        auto& request_threads = thread_context.request_threads;
98
4
        ConnThread request_thread;
99
4
        bool inserted{false};
100
4
        Mutex cancel_mutex;
101
4
        Lock cancel_lock{cancel_mutex};
102
4
        server_context.cancel_lock = &cancel_lock;
103
4
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
4
            if (cancel_monitor.m_canceled) {
106
4
                server_context.request_canceled = true;
107
4
                return;
108
4
            }
109
            // Detect request being canceled while it executes.
110
4
            assert(!cancel_monitor.m_on_cancel);
111
4
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
4
                Lock cancel_lock{cancel_mutex};
125
4
                server_context.request_canceled = true;
126
4
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
4
            std::tie(request_thread, inserted) = SetThread(
132
4
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
4
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
4
        const bool erase_thread{inserted};
142
4
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
4
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
4
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
4
                cancel_monitor.m_on_cancel = nullptr;
166
4
                auto self_dispose{kj::mv(self)};
167
4
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
4
                    ConnThreads::node_type removed;
175
4
                    {
176
4
                        Lock lock(thread_context.waiter->m_mutex);
177
4
                        removed = request_threads.extract(server.m_context.connection);
178
4
                    }
179
4
                }
180
4
            });
181
4
        );
182
4
        if (server_context.request_canceled) {
183
4
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
4
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
4
            try {
186
4
                fn.invoke(server_context, args...);
187
4
            } catch (const InterruptException& e) {
188
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
4
            }
190
4
        })) {
191
4
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
4
            kj::throwRecoverableException(kj::mv(*exception));
193
4
        }
194
4
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
4
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
4
    const auto& params = server_context.call_context.getParams();
202
4
    Context::Reader context_arg = Accessor::get(params);
203
4
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
4
    } else {
234
4
        auto thread_client = context_arg.getThread();
235
4
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
4
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
4
                KJ_IF_MAYBE (thread_server, perhaps) {
241
4
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
4
                    MP_LOG(loop, Log::Debug)
243
4
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
4
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
4
                } else {
246
4
                    MP_LOG(loop, Log::Error)
247
4
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
4
                    throw std::runtime_error("invalid thread handle");
249
4
                }
250
4
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
4
                MP_LOG(loop, Log::Info)
282
4
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
4
                return kj::mv(e);
284
4
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
4
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
4
    }
292
4
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsTestChainParams, ipc::capnp::messages::Mining::IsTestChainResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
1
{
64
1
    auto& server = server_context.proxy_server;
65
1
    EventLoop& loop = *server.m_context.loop;
66
1
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
1
    auto self = server.thisCap();
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
1
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
1
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
1
            kj::throwRecoverableException(kj::mv(*exception));
193
1
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
1
    const auto& params = server_context.call_context.getParams();
202
1
    Context::Reader context_arg = Accessor::get(params);
203
1
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
1
    } else {
234
1
        auto thread_client = context_arg.getThread();
235
1
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
1
                    MP_LOG(loop, Log::Error)
247
1
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
1
                    throw std::runtime_error("invalid thread handle");
249
1
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
1
                MP_LOG(loop, Log::Info)
282
1
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
1
                return kj::mv(e);
284
1
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
1
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
1
    }
292
1
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::IsInitialBlockDownloadParams, ipc::capnp::messages::Mining::IsInitialBlockDownloadResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
1
{
64
1
    auto& server = server_context.proxy_server;
65
1
    EventLoop& loop = *server.m_context.loop;
66
1
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
1
    auto self = server.thisCap();
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
1
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
1
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
1
            kj::throwRecoverableException(kj::mv(*exception));
193
1
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
1
    const auto& params = server_context.call_context.getParams();
202
1
    Context::Reader context_arg = Accessor::get(params);
203
1
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
1
    } else {
234
1
        auto thread_client = context_arg.getThread();
235
1
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
1
                    MP_LOG(loop, Log::Error)
247
1
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
1
                    throw std::runtime_error("invalid thread handle");
249
1
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
1
                MP_LOG(loop, Log::Info)
282
1
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
1
                return kj::mv(e);
284
1
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
1
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
1
    }
292
1
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTipParams, ipc::capnp::messages::Mining::GetTipResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 22>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
2
{
64
2
    auto& server = server_context.proxy_server;
65
2
    EventLoop& loop = *server.m_context.loop;
66
2
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
2
    auto self = server.thisCap();
74
2
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
2
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
2
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
2
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
2
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
2
        auto& thread_context = g_thread_context;
97
2
        auto& request_threads = thread_context.request_threads;
98
2
        ConnThread request_thread;
99
2
        bool inserted{false};
100
2
        Mutex cancel_mutex;
101
2
        Lock cancel_lock{cancel_mutex};
102
2
        server_context.cancel_lock = &cancel_lock;
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
2
                server_context.request_canceled = true;
107
2
                return;
108
2
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
2
        const bool erase_thread{inserted};
142
2
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
2
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
2
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
2
                cancel_monitor.m_on_cancel = nullptr;
166
2
                auto self_dispose{kj::mv(self)};
167
2
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
2
                    ConnThreads::node_type removed;
175
2
                    {
176
2
                        Lock lock(thread_context.waiter->m_mutex);
177
2
                        removed = request_threads.extract(server.m_context.connection);
178
2
                    }
179
2
                }
180
2
            });
181
2
        );
182
2
        if (server_context.request_canceled) {
183
2
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
2
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
2
            try {
186
2
                fn.invoke(server_context, args...);
187
2
            } catch (const InterruptException& e) {
188
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
2
            }
190
2
        })) {
191
2
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
2
            kj::throwRecoverableException(kj::mv(*exception));
193
2
        }
194
2
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
2
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
2
    const auto& params = server_context.call_context.getParams();
202
2
    Context::Reader context_arg = Accessor::get(params);
203
2
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
2
    } else {
234
2
        auto thread_client = context_arg.getThread();
235
2
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
2
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
2
                KJ_IF_MAYBE (thread_server, perhaps) {
241
2
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
2
                    MP_LOG(loop, Log::Debug)
243
2
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
2
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
2
                } else {
246
2
                    MP_LOG(loop, Log::Error)
247
2
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
2
                    throw std::runtime_error("invalid thread handle");
249
2
                }
250
2
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
2
                MP_LOG(loop, Log::Info)
282
2
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
2
                return kj::mv(e);
284
2
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
2
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
2
    }
292
2
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::WaitTipChangedParams, ipc::capnp::messages::Mining::WaitTipChangedResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::CurrentTip, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timeout, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<uint256, std::chrono::duration<double, std::ratio<1l, 1000l>>>&&)
Line
Count
Source
63
3
{
64
3
    auto& server = server_context.proxy_server;
65
3
    EventLoop& loop = *server.m_context.loop;
66
3
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
3
    auto self = server.thisCap();
74
3
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
3
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
3
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
3
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
3
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
3
        auto& thread_context = g_thread_context;
97
3
        auto& request_threads = thread_context.request_threads;
98
3
        ConnThread request_thread;
99
3
        bool inserted{false};
100
3
        Mutex cancel_mutex;
101
3
        Lock cancel_lock{cancel_mutex};
102
3
        server_context.cancel_lock = &cancel_lock;
103
3
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
3
            if (cancel_monitor.m_canceled) {
106
3
                server_context.request_canceled = true;
107
3
                return;
108
3
            }
109
            // Detect request being canceled while it executes.
110
3
            assert(!cancel_monitor.m_on_cancel);
111
3
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
3
                Lock cancel_lock{cancel_mutex};
125
3
                server_context.request_canceled = true;
126
3
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
3
            std::tie(request_thread, inserted) = SetThread(
132
3
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
3
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
3
        const bool erase_thread{inserted};
142
3
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
3
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
3
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
3
                cancel_monitor.m_on_cancel = nullptr;
166
3
                auto self_dispose{kj::mv(self)};
167
3
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
3
                    ConnThreads::node_type removed;
175
3
                    {
176
3
                        Lock lock(thread_context.waiter->m_mutex);
177
3
                        removed = request_threads.extract(server.m_context.connection);
178
3
                    }
179
3
                }
180
3
            });
181
3
        );
182
3
        if (server_context.request_canceled) {
183
3
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
3
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
3
            try {
186
3
                fn.invoke(server_context, args...);
187
3
            } catch (const InterruptException& e) {
188
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
3
            }
190
3
        })) {
191
3
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
3
            kj::throwRecoverableException(kj::mv(*exception));
193
3
        }
194
3
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
3
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
3
    const auto& params = server_context.call_context.getParams();
202
3
    Context::Reader context_arg = Accessor::get(params);
203
3
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
3
    } else {
234
3
        auto thread_client = context_arg.getThread();
235
3
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
3
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
3
                KJ_IF_MAYBE (thread_server, perhaps) {
241
3
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
3
                    MP_LOG(loop, Log::Debug)
243
3
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
3
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
3
                } else {
246
3
                    MP_LOG(loop, Log::Error)
247
3
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
3
                    throw std::runtime_error("invalid thread handle");
249
3
                }
250
3
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
3
                MP_LOG(loop, Log::Info)
282
3
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
3
                return kj::mv(e);
284
3
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
3
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
3
    }
292
3
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>>, mp::TypeList<node::BlockCreateOptions const&, bool>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CreateNewBlockParams, ipc::capnp::messages::Mining::CreateNewBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Cooldown, 1>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>> const&, mp::TypeList<node::BlockCreateOptions const&, bool>&&)
Line
Count
Source
63
37
{
64
37
    auto& server = server_context.proxy_server;
65
37
    EventLoop& loop = *server.m_context.loop;
66
37
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
37
    auto self = server.thisCap();
74
37
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
37
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
37
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
37
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
37
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
37
        auto& thread_context = g_thread_context;
97
37
        auto& request_threads = thread_context.request_threads;
98
37
        ConnThread request_thread;
99
37
        bool inserted{false};
100
37
        Mutex cancel_mutex;
101
37
        Lock cancel_lock{cancel_mutex};
102
37
        server_context.cancel_lock = &cancel_lock;
103
37
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
37
            if (cancel_monitor.m_canceled) {
106
37
                server_context.request_canceled = true;
107
37
                return;
108
37
            }
109
            // Detect request being canceled while it executes.
110
37
            assert(!cancel_monitor.m_on_cancel);
111
37
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
37
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
37
                Lock cancel_lock{cancel_mutex};
125
37
                server_context.request_canceled = true;
126
37
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
37
            std::tie(request_thread, inserted) = SetThread(
132
37
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
37
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
37
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
37
        const bool erase_thread{inserted};
142
37
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
37
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
37
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
37
                cancel_monitor.m_on_cancel = nullptr;
166
37
                auto self_dispose{kj::mv(self)};
167
37
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
37
                    ConnThreads::node_type removed;
175
37
                    {
176
37
                        Lock lock(thread_context.waiter->m_mutex);
177
37
                        removed = request_threads.extract(server.m_context.connection);
178
37
                    }
179
37
                }
180
37
            });
181
37
        );
182
37
        if (server_context.request_canceled) {
183
37
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
37
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
37
            try {
186
37
                fn.invoke(server_context, args...);
187
37
            } catch (const InterruptException& e) {
188
37
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
37
            }
190
37
        })) {
191
37
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
37
            kj::throwRecoverableException(kj::mv(*exception));
193
37
        }
194
37
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
37
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
37
    const auto& params = server_context.call_context.getParams();
202
37
    Context::Reader context_arg = Accessor::get(params);
203
37
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
37
    } else {
234
37
        auto thread_client = context_arg.getThread();
235
37
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
37
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
37
                KJ_IF_MAYBE (thread_server, perhaps) {
241
37
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
37
                    MP_LOG(loop, Log::Debug)
243
37
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
37
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
37
                } else {
246
37
                    MP_LOG(loop, Log::Error)
247
37
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
37
                    throw std::runtime_error("invalid thread handle");
249
37
                }
250
37
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
37
                MP_LOG(loop, Log::Info)
282
37
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
37
                return kj::mv(e);
284
37
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
37
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
37
    }
292
37
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::CheckBlockParams, ipc::capnp::messages::Mining::CheckBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<CBlock const&, node::BlockCheckOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)
Line
Count
Source
63
3
{
64
3
    auto& server = server_context.proxy_server;
65
3
    EventLoop& loop = *server.m_context.loop;
66
3
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
3
    auto self = server.thisCap();
74
3
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
3
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
3
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
3
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
3
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
3
        auto& thread_context = g_thread_context;
97
3
        auto& request_threads = thread_context.request_threads;
98
3
        ConnThread request_thread;
99
3
        bool inserted{false};
100
3
        Mutex cancel_mutex;
101
3
        Lock cancel_lock{cancel_mutex};
102
3
        server_context.cancel_lock = &cancel_lock;
103
3
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
3
            if (cancel_monitor.m_canceled) {
106
3
                server_context.request_canceled = true;
107
3
                return;
108
3
            }
109
            // Detect request being canceled while it executes.
110
3
            assert(!cancel_monitor.m_on_cancel);
111
3
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
3
                Lock cancel_lock{cancel_mutex};
125
3
                server_context.request_canceled = true;
126
3
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
3
            std::tie(request_thread, inserted) = SetThread(
132
3
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
3
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
3
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
3
        const bool erase_thread{inserted};
142
3
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
3
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
3
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
3
                cancel_monitor.m_on_cancel = nullptr;
166
3
                auto self_dispose{kj::mv(self)};
167
3
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
3
                    ConnThreads::node_type removed;
175
3
                    {
176
3
                        Lock lock(thread_context.waiter->m_mutex);
177
3
                        removed = request_threads.extract(server.m_context.connection);
178
3
                    }
179
3
                }
180
3
            });
181
3
        );
182
3
        if (server_context.request_canceled) {
183
3
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
3
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
3
            try {
186
3
                fn.invoke(server_context, args...);
187
3
            } catch (const InterruptException& e) {
188
3
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
3
            }
190
3
        })) {
191
3
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
3
            kj::throwRecoverableException(kj::mv(*exception));
193
3
        }
194
3
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
3
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
3
    const auto& params = server_context.call_context.getParams();
202
3
    Context::Reader context_arg = Accessor::get(params);
203
3
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
3
    } else {
234
3
        auto thread_client = context_arg.getThread();
235
3
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
3
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
3
                KJ_IF_MAYBE (thread_server, perhaps) {
241
3
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
3
                    MP_LOG(loop, Log::Debug)
243
3
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
3
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
3
                } else {
246
3
                    MP_LOG(loop, Log::Error)
247
3
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
3
                    throw std::runtime_error("invalid thread handle");
249
3
                }
250
3
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
3
                MP_LOG(loop, Log::Info)
282
3
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
3
                return kj::mv(e);
284
3
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
3
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
3
    }
292
3
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::SubmitBlockParams, ipc::capnp::messages::Mining::SubmitBlockResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Block, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>> const&, mp::TypeList<CBlock const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)
Line
Count
Source
63
12
{
64
12
    auto& server = server_context.proxy_server;
65
12
    EventLoop& loop = *server.m_context.loop;
66
12
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
12
    auto self = server.thisCap();
74
12
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
12
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
12
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
12
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
12
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
12
        auto& thread_context = g_thread_context;
97
12
        auto& request_threads = thread_context.request_threads;
98
12
        ConnThread request_thread;
99
12
        bool inserted{false};
100
12
        Mutex cancel_mutex;
101
12
        Lock cancel_lock{cancel_mutex};
102
12
        server_context.cancel_lock = &cancel_lock;
103
12
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
12
            if (cancel_monitor.m_canceled) {
106
12
                server_context.request_canceled = true;
107
12
                return;
108
12
            }
109
            // Detect request being canceled while it executes.
110
12
            assert(!cancel_monitor.m_on_cancel);
111
12
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
12
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
12
                Lock cancel_lock{cancel_mutex};
125
12
                server_context.request_canceled = true;
126
12
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
12
            std::tie(request_thread, inserted) = SetThread(
132
12
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
12
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
12
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
12
        const bool erase_thread{inserted};
142
12
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
12
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
12
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
12
                cancel_monitor.m_on_cancel = nullptr;
166
12
                auto self_dispose{kj::mv(self)};
167
12
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
12
                    ConnThreads::node_type removed;
175
12
                    {
176
12
                        Lock lock(thread_context.waiter->m_mutex);
177
12
                        removed = request_threads.extract(server.m_context.connection);
178
12
                    }
179
12
                }
180
12
            });
181
12
        );
182
12
        if (server_context.request_canceled) {
183
12
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
12
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
12
            try {
186
12
                fn.invoke(server_context, args...);
187
12
            } catch (const InterruptException& e) {
188
12
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
12
            }
190
12
        })) {
191
12
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
12
            kj::throwRecoverableException(kj::mv(*exception));
193
12
        }
194
12
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
12
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
12
    const auto& params = server_context.call_context.getParams();
202
12
    Context::Reader context_arg = Accessor::get(params);
203
12
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
12
    } else {
234
12
        auto thread_client = context_arg.getThread();
235
12
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
12
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
12
                KJ_IF_MAYBE (thread_server, perhaps) {
241
12
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
12
                    MP_LOG(loop, Log::Debug)
243
12
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
12
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
12
                } else {
246
12
                    MP_LOG(loop, Log::Error)
247
12
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
12
                    throw std::runtime_error("invalid thread handle");
249
12
                }
250
12
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
12
                MP_LOG(loop, Log::Info)
282
12
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
12
                return kj::mv(e);
284
12
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
12
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
12
    }
292
12
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByTxIDParams, ipc::capnp::messages::Mining::GetTransactionsByTxIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Txids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<false>, std::allocator<transaction_identifier<false>>> const&>&&)
Line
Count
Source
63
2
{
64
2
    auto& server = server_context.proxy_server;
65
2
    EventLoop& loop = *server.m_context.loop;
66
2
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
2
    auto self = server.thisCap();
74
2
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
2
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
2
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
2
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
2
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
2
        auto& thread_context = g_thread_context;
97
2
        auto& request_threads = thread_context.request_threads;
98
2
        ConnThread request_thread;
99
2
        bool inserted{false};
100
2
        Mutex cancel_mutex;
101
2
        Lock cancel_lock{cancel_mutex};
102
2
        server_context.cancel_lock = &cancel_lock;
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
2
                server_context.request_canceled = true;
107
2
                return;
108
2
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
2
        const bool erase_thread{inserted};
142
2
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
2
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
2
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
2
                cancel_monitor.m_on_cancel = nullptr;
166
2
                auto self_dispose{kj::mv(self)};
167
2
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
2
                    ConnThreads::node_type removed;
175
2
                    {
176
2
                        Lock lock(thread_context.waiter->m_mutex);
177
2
                        removed = request_threads.extract(server.m_context.connection);
178
2
                    }
179
2
                }
180
2
            });
181
2
        );
182
2
        if (server_context.request_canceled) {
183
2
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
2
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
2
            try {
186
2
                fn.invoke(server_context, args...);
187
2
            } catch (const InterruptException& e) {
188
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
2
            }
190
2
        })) {
191
2
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
2
            kj::throwRecoverableException(kj::mv(*exception));
193
2
        }
194
2
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
2
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
2
    const auto& params = server_context.call_context.getParams();
202
2
    Context::Reader context_arg = Accessor::get(params);
203
2
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
2
    } else {
234
2
        auto thread_client = context_arg.getThread();
235
2
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
2
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
2
                KJ_IF_MAYBE (thread_server, perhaps) {
241
2
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
2
                    MP_LOG(loop, Log::Debug)
243
2
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
2
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
2
                } else {
246
2
                    MP_LOG(loop, Log::Error)
247
2
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
2
                    throw std::runtime_error("invalid thread handle");
249
2
                }
250
2
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
2
                MP_LOG(loop, Log::Info)
282
2
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
2
                return kj::mv(e);
284
2
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
2
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
2
    }
292
2
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Mining>, capnp::CallContext<ipc::capnp::messages::Mining::GetTransactionsByWitnessIDParams, ipc::capnp::messages::Mining::GetTransactionsByWitnessIDResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Wtxids, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::vector<transaction_identifier<true>, std::allocator<transaction_identifier<true>>> const&>&&)
Line
Count
Source
63
2
{
64
2
    auto& server = server_context.proxy_server;
65
2
    EventLoop& loop = *server.m_context.loop;
66
2
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
2
    auto self = server.thisCap();
74
2
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
2
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
2
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
2
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
2
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
2
        auto& thread_context = g_thread_context;
97
2
        auto& request_threads = thread_context.request_threads;
98
2
        ConnThread request_thread;
99
2
        bool inserted{false};
100
2
        Mutex cancel_mutex;
101
2
        Lock cancel_lock{cancel_mutex};
102
2
        server_context.cancel_lock = &cancel_lock;
103
2
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
2
            if (cancel_monitor.m_canceled) {
106
2
                server_context.request_canceled = true;
107
2
                return;
108
2
            }
109
            // Detect request being canceled while it executes.
110
2
            assert(!cancel_monitor.m_on_cancel);
111
2
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
2
                Lock cancel_lock{cancel_mutex};
125
2
                server_context.request_canceled = true;
126
2
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
2
            std::tie(request_thread, inserted) = SetThread(
132
2
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
2
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
2
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
2
        const bool erase_thread{inserted};
142
2
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
2
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
2
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
2
                cancel_monitor.m_on_cancel = nullptr;
166
2
                auto self_dispose{kj::mv(self)};
167
2
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
2
                    ConnThreads::node_type removed;
175
2
                    {
176
2
                        Lock lock(thread_context.waiter->m_mutex);
177
2
                        removed = request_threads.extract(server.m_context.connection);
178
2
                    }
179
2
                }
180
2
            });
181
2
        );
182
2
        if (server_context.request_canceled) {
183
2
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
2
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
2
            try {
186
2
                fn.invoke(server_context, args...);
187
2
            } catch (const InterruptException& e) {
188
2
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
2
            }
190
2
        })) {
191
2
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
2
            kj::throwRecoverableException(kj::mv(*exception));
193
2
        }
194
2
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
2
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
2
    const auto& params = server_context.call_context.getParams();
202
2
    Context::Reader context_arg = Accessor::get(params);
203
2
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
2
    } else {
234
2
        auto thread_client = context_arg.getThread();
235
2
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
2
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
2
                KJ_IF_MAYBE (thread_server, perhaps) {
241
2
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
2
                    MP_LOG(loop, Log::Debug)
243
2
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
2
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
2
                } else {
246
2
                    MP_LOG(loop, Log::Error)
247
2
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
2
                    throw std::runtime_error("invalid thread handle");
249
2
                }
250
2
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
2
                MP_LOG(loop, Log::Info)
282
2
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
2
                return kj::mv(e);
284
2
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
2
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
2
    }
292
2
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::DestroyParams, ipc::capnp::messages::BlockTemplate::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)
Line
Count
Source
63
39
{
64
39
    auto& server = server_context.proxy_server;
65
39
    EventLoop& loop = *server.m_context.loop;
66
39
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
39
    auto self = server.thisCap();
74
39
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
39
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
39
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
39
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
39
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
39
        auto& thread_context = g_thread_context;
97
39
        auto& request_threads = thread_context.request_threads;
98
39
        ConnThread request_thread;
99
39
        bool inserted{false};
100
39
        Mutex cancel_mutex;
101
39
        Lock cancel_lock{cancel_mutex};
102
39
        server_context.cancel_lock = &cancel_lock;
103
39
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
39
            if (cancel_monitor.m_canceled) {
106
39
                server_context.request_canceled = true;
107
39
                return;
108
39
            }
109
            // Detect request being canceled while it executes.
110
39
            assert(!cancel_monitor.m_on_cancel);
111
39
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
39
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
39
                Lock cancel_lock{cancel_mutex};
125
39
                server_context.request_canceled = true;
126
39
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
39
            std::tie(request_thread, inserted) = SetThread(
132
39
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
39
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
39
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
39
        const bool erase_thread{inserted};
142
39
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
39
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
39
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
39
                cancel_monitor.m_on_cancel = nullptr;
166
39
                auto self_dispose{kj::mv(self)};
167
39
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
39
                    ConnThreads::node_type removed;
175
39
                    {
176
39
                        Lock lock(thread_context.waiter->m_mutex);
177
39
                        removed = request_threads.extract(server.m_context.connection);
178
39
                    }
179
39
                }
180
39
            });
181
39
        );
182
39
        if (server_context.request_canceled) {
183
39
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
39
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
39
            try {
186
39
                fn.invoke(server_context, args...);
187
39
            } catch (const InterruptException& e) {
188
39
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
39
            }
190
39
        })) {
191
39
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
39
            kj::throwRecoverableException(kj::mv(*exception));
193
39
        }
194
39
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
39
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
39
    const auto& params = server_context.call_context.getParams();
202
39
    Context::Reader context_arg = Accessor::get(params);
203
39
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
39
    } else {
234
39
        auto thread_client = context_arg.getThread();
235
39
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
39
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
39
                KJ_IF_MAYBE (thread_server, perhaps) {
241
39
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
39
                    MP_LOG(loop, Log::Debug)
243
39
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
39
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
39
                } else {
246
39
                    MP_LOG(loop, Log::Error)
247
39
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
39
                    throw std::runtime_error("invalid thread handle");
249
39
                }
250
39
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
39
                MP_LOG(loop, Log::Info)
282
39
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
39
                return kj::mv(e);
284
39
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
39
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
39
    }
292
39
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockHeaderParams, ipc::capnp::messages::BlockTemplate::GetBlockHeaderResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
1
{
64
1
    auto& server = server_context.proxy_server;
65
1
    EventLoop& loop = *server.m_context.loop;
66
1
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
1
    auto self = server.thisCap();
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
1
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
1
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
1
            kj::throwRecoverableException(kj::mv(*exception));
193
1
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
1
    const auto& params = server_context.call_context.getParams();
202
1
    Context::Reader context_arg = Accessor::get(params);
203
1
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
1
    } else {
234
1
        auto thread_client = context_arg.getThread();
235
1
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
1
                    MP_LOG(loop, Log::Error)
247
1
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
1
                    throw std::runtime_error("invalid thread handle");
249
1
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
1
                MP_LOG(loop, Log::Info)
282
1
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
1
                return kj::mv(e);
284
1
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
1
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
1
    }
292
1
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetBlockParams, ipc::capnp::messages::BlockTemplate::GetBlockResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
36
{
64
36
    auto& server = server_context.proxy_server;
65
36
    EventLoop& loop = *server.m_context.loop;
66
36
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
36
    auto self = server.thisCap();
74
36
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
36
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
36
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
36
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
36
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
36
        auto& thread_context = g_thread_context;
97
36
        auto& request_threads = thread_context.request_threads;
98
36
        ConnThread request_thread;
99
36
        bool inserted{false};
100
36
        Mutex cancel_mutex;
101
36
        Lock cancel_lock{cancel_mutex};
102
36
        server_context.cancel_lock = &cancel_lock;
103
36
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
36
            if (cancel_monitor.m_canceled) {
106
36
                server_context.request_canceled = true;
107
36
                return;
108
36
            }
109
            // Detect request being canceled while it executes.
110
36
            assert(!cancel_monitor.m_on_cancel);
111
36
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
36
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
36
                Lock cancel_lock{cancel_mutex};
125
36
                server_context.request_canceled = true;
126
36
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
36
            std::tie(request_thread, inserted) = SetThread(
132
36
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
36
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
36
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
36
        const bool erase_thread{inserted};
142
36
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
36
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
36
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
36
                cancel_monitor.m_on_cancel = nullptr;
166
36
                auto self_dispose{kj::mv(self)};
167
36
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
36
                    ConnThreads::node_type removed;
175
36
                    {
176
36
                        Lock lock(thread_context.waiter->m_mutex);
177
36
                        removed = request_threads.extract(server.m_context.connection);
178
36
                    }
179
36
                }
180
36
            });
181
36
        );
182
36
        if (server_context.request_canceled) {
183
36
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
36
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
36
            try {
186
36
                fn.invoke(server_context, args...);
187
36
            } catch (const InterruptException& e) {
188
36
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
36
            }
190
36
        })) {
191
36
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
36
            kj::throwRecoverableException(kj::mv(*exception));
193
36
        }
194
36
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
36
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
36
    const auto& params = server_context.call_context.getParams();
202
36
    Context::Reader context_arg = Accessor::get(params);
203
36
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
36
    } else {
234
36
        auto thread_client = context_arg.getThread();
235
36
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
36
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
36
                KJ_IF_MAYBE (thread_server, perhaps) {
241
36
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
36
                    MP_LOG(loop, Log::Debug)
243
36
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
36
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
36
                } else {
246
36
                    MP_LOG(loop, Log::Error)
247
36
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
36
                    throw std::runtime_error("invalid thread handle");
249
36
                }
250
36
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
36
                MP_LOG(loop, Log::Info)
282
36
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
36
                return kj::mv(e);
284
36
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
36
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
36
    }
292
36
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxFeesParams, ipc::capnp::messages::BlockTemplate::GetTxFeesResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
1
{
64
1
    auto& server = server_context.proxy_server;
65
1
    EventLoop& loop = *server.m_context.loop;
66
1
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
1
    auto self = server.thisCap();
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
1
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
1
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
1
            kj::throwRecoverableException(kj::mv(*exception));
193
1
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
1
    const auto& params = server_context.call_context.getParams();
202
1
    Context::Reader context_arg = Accessor::get(params);
203
1
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
1
    } else {
234
1
        auto thread_client = context_arg.getThread();
235
1
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
1
                    MP_LOG(loop, Log::Error)
247
1
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
1
                    throw std::runtime_error("invalid thread handle");
249
1
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
1
                MP_LOG(loop, Log::Info)
282
1
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
1
                return kj::mv(e);
284
1
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
1
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
1
    }
292
1
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetTxSigopsParams, ipc::capnp::messages::BlockTemplate::GetTxSigopsResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
1
{
64
1
    auto& server = server_context.proxy_server;
65
1
    EventLoop& loop = *server.m_context.loop;
66
1
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
1
    auto self = server.thisCap();
74
1
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
1
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
1
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
1
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
1
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
1
        auto& thread_context = g_thread_context;
97
1
        auto& request_threads = thread_context.request_threads;
98
1
        ConnThread request_thread;
99
1
        bool inserted{false};
100
1
        Mutex cancel_mutex;
101
1
        Lock cancel_lock{cancel_mutex};
102
1
        server_context.cancel_lock = &cancel_lock;
103
1
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
1
            if (cancel_monitor.m_canceled) {
106
1
                server_context.request_canceled = true;
107
1
                return;
108
1
            }
109
            // Detect request being canceled while it executes.
110
1
            assert(!cancel_monitor.m_on_cancel);
111
1
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
1
                Lock cancel_lock{cancel_mutex};
125
1
                server_context.request_canceled = true;
126
1
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
1
            std::tie(request_thread, inserted) = SetThread(
132
1
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
1
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
1
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
1
        const bool erase_thread{inserted};
142
1
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
1
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
1
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
1
                cancel_monitor.m_on_cancel = nullptr;
166
1
                auto self_dispose{kj::mv(self)};
167
1
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
1
                    ConnThreads::node_type removed;
175
1
                    {
176
1
                        Lock lock(thread_context.waiter->m_mutex);
177
1
                        removed = request_threads.extract(server.m_context.connection);
178
1
                    }
179
1
                }
180
1
            });
181
1
        );
182
1
        if (server_context.request_canceled) {
183
1
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
1
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
1
            try {
186
1
                fn.invoke(server_context, args...);
187
1
            } catch (const InterruptException& e) {
188
1
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
1
            }
190
1
        })) {
191
1
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
1
            kj::throwRecoverableException(kj::mv(*exception));
193
1
        }
194
1
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
1
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
1
    const auto& params = server_context.call_context.getParams();
202
1
    Context::Reader context_arg = Accessor::get(params);
203
1
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
1
    } else {
234
1
        auto thread_client = context_arg.getThread();
235
1
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
1
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
1
                KJ_IF_MAYBE (thread_server, perhaps) {
241
1
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
1
                    MP_LOG(loop, Log::Debug)
243
1
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
1
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
1
                } else {
246
1
                    MP_LOG(loop, Log::Error)
247
1
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
1
                    throw std::runtime_error("invalid thread handle");
249
1
                }
250
1
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
1
                MP_LOG(loop, Log::Info)
282
1
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
1
                return kj::mv(e);
284
1
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
1
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
1
    }
292
1
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseTxParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseTxResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Line
Count
Source
63
23
{
64
23
    auto& server = server_context.proxy_server;
65
23
    EventLoop& loop = *server.m_context.loop;
66
23
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
23
    auto self = server.thisCap();
74
23
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
23
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
23
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
23
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
23
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
23
        auto& thread_context = g_thread_context;
97
23
        auto& request_threads = thread_context.request_threads;
98
23
        ConnThread request_thread;
99
23
        bool inserted{false};
100
23
        Mutex cancel_mutex;
101
23
        Lock cancel_lock{cancel_mutex};
102
23
        server_context.cancel_lock = &cancel_lock;
103
23
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
23
            if (cancel_monitor.m_canceled) {
106
23
                server_context.request_canceled = true;
107
23
                return;
108
23
            }
109
            // Detect request being canceled while it executes.
110
23
            assert(!cancel_monitor.m_on_cancel);
111
23
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
23
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
23
                Lock cancel_lock{cancel_mutex};
125
23
                server_context.request_canceled = true;
126
23
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
23
            std::tie(request_thread, inserted) = SetThread(
132
23
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
23
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
23
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
23
        const bool erase_thread{inserted};
142
23
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
23
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
23
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
23
                cancel_monitor.m_on_cancel = nullptr;
166
23
                auto self_dispose{kj::mv(self)};
167
23
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
23
                    ConnThreads::node_type removed;
175
23
                    {
176
23
                        Lock lock(thread_context.waiter->m_mutex);
177
23
                        removed = request_threads.extract(server.m_context.connection);
178
23
                    }
179
23
                }
180
23
            });
181
23
        );
182
23
        if (server_context.request_canceled) {
183
23
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
23
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
23
            try {
186
23
                fn.invoke(server_context, args...);
187
23
            } catch (const InterruptException& e) {
188
23
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
23
            }
190
23
        })) {
191
23
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
23
            kj::throwRecoverableException(kj::mv(*exception));
193
23
        }
194
23
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
23
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
23
    const auto& params = server_context.call_context.getParams();
202
23
    Context::Reader context_arg = Accessor::get(params);
203
23
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
23
    } else {
234
23
        auto thread_client = context_arg.getThread();
235
23
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
23
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
23
                KJ_IF_MAYBE (thread_server, perhaps) {
241
23
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
23
                    MP_LOG(loop, Log::Debug)
243
23
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
23
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
23
                } else {
246
23
                    MP_LOG(loop, Log::Error)
247
23
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
23
                    throw std::runtime_error("invalid thread handle");
249
23
                }
250
23
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
23
                MP_LOG(loop, Log::Info)
282
23
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
23
                return kj::mv(e);
284
23
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
23
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
23
    }
292
23
}
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathParams, ipc::capnp::messages::BlockTemplate::GetCoinbaseMerklePathResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)
Unexecuted instantiation: std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Params, ipc::capnp::messages::BlockTemplate::SubmitSolutionOld7Results>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>>&&)
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)
Line
Count
Source
63
13
{
64
13
    auto& server = server_context.proxy_server;
65
13
    EventLoop& loop = *server.m_context.loop;
66
13
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
13
    auto self = server.thisCap();
74
13
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
13
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
13
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
13
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
13
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
13
        auto& thread_context = g_thread_context;
97
13
        auto& request_threads = thread_context.request_threads;
98
13
        ConnThread request_thread;
99
13
        bool inserted{false};
100
13
        Mutex cancel_mutex;
101
13
        Lock cancel_lock{cancel_mutex};
102
13
        server_context.cancel_lock = &cancel_lock;
103
13
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
13
            if (cancel_monitor.m_canceled) {
106
13
                server_context.request_canceled = true;
107
13
                return;
108
13
            }
109
            // Detect request being canceled while it executes.
110
13
            assert(!cancel_monitor.m_on_cancel);
111
13
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
13
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
13
                Lock cancel_lock{cancel_mutex};
125
13
                server_context.request_canceled = true;
126
13
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
13
            std::tie(request_thread, inserted) = SetThread(
132
13
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
13
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
13
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
13
        const bool erase_thread{inserted};
142
13
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
13
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
13
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
13
                cancel_monitor.m_on_cancel = nullptr;
166
13
                auto self_dispose{kj::mv(self)};
167
13
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
13
                    ConnThreads::node_type removed;
175
13
                    {
176
13
                        Lock lock(thread_context.waiter->m_mutex);
177
13
                        removed = request_threads.extract(server.m_context.connection);
178
13
                    }
179
13
                }
180
13
            });
181
13
        );
182
13
        if (server_context.request_canceled) {
183
13
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
13
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
13
            try {
186
13
                fn.invoke(server_context, args...);
187
13
            } catch (const InterruptException& e) {
188
13
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
13
            }
190
13
        })) {
191
13
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
13
            kj::throwRecoverableException(kj::mv(*exception));
193
13
        }
194
13
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
13
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
13
    const auto& params = server_context.call_context.getParams();
202
13
    Context::Reader context_arg = Accessor::get(params);
203
13
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
13
    } else {
234
13
        auto thread_client = context_arg.getThread();
235
13
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
13
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
13
                KJ_IF_MAYBE (thread_server, perhaps) {
241
13
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
13
                    MP_LOG(loop, Log::Debug)
243
13
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
13
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
13
                } else {
246
13
                    MP_LOG(loop, Log::Error)
247
13
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
13
                    throw std::runtime_error("invalid thread handle");
249
13
                }
250
13
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
13
                MP_LOG(loop, Log::Info)
282
13
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
13
                return kj::mv(e);
284
13
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
13
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
13
    }
292
13
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>>, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::SubmitSolutionParams, ipc::capnp::messages::BlockTemplate::SubmitSolutionResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Version, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Timestamp, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Nonce, 1>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Coinbase, 17>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Reason, 18>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Debug, 18>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 2>, mp::ServerCall>>>>>>> const&, mp::TypeList<unsigned int, unsigned int, unsigned int, std::shared_ptr<CTransaction const>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>&&)
Line
Count
Source
63
24
{
64
24
    auto& server = server_context.proxy_server;
65
24
    EventLoop& loop = *server.m_context.loop;
66
24
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
24
    auto self = server.thisCap();
74
24
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
24
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
24
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
24
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
24
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
24
        auto& thread_context = g_thread_context;
97
24
        auto& request_threads = thread_context.request_threads;
98
24
        ConnThread request_thread;
99
24
        bool inserted{false};
100
24
        Mutex cancel_mutex;
101
24
        Lock cancel_lock{cancel_mutex};
102
24
        server_context.cancel_lock = &cancel_lock;
103
24
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
24
            if (cancel_monitor.m_canceled) {
106
24
                server_context.request_canceled = true;
107
24
                return;
108
24
            }
109
            // Detect request being canceled while it executes.
110
24
            assert(!cancel_monitor.m_on_cancel);
111
24
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
24
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
24
                Lock cancel_lock{cancel_mutex};
125
24
                server_context.request_canceled = true;
126
24
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
24
            std::tie(request_thread, inserted) = SetThread(
132
24
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
24
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
24
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
24
        const bool erase_thread{inserted};
142
24
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
24
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
24
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
24
                cancel_monitor.m_on_cancel = nullptr;
166
24
                auto self_dispose{kj::mv(self)};
167
24
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
24
                    ConnThreads::node_type removed;
175
24
                    {
176
24
                        Lock lock(thread_context.waiter->m_mutex);
177
24
                        removed = request_threads.extract(server.m_context.connection);
178
24
                    }
179
24
                }
180
24
            });
181
24
        );
182
24
        if (server_context.request_canceled) {
183
24
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
24
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
24
            try {
186
24
                fn.invoke(server_context, args...);
187
24
            } catch (const InterruptException& e) {
188
24
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
24
            }
190
24
        })) {
191
24
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
24
            kj::throwRecoverableException(kj::mv(*exception));
193
24
        }
194
24
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
24
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
24
    const auto& params = server_context.call_context.getParams();
202
24
    Context::Reader context_arg = Accessor::get(params);
203
24
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
24
    } else {
234
24
        auto thread_client = context_arg.getThread();
235
24
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
24
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
24
                KJ_IF_MAYBE (thread_server, perhaps) {
241
24
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
24
                    MP_LOG(loop, Log::Debug)
243
24
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
24
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
24
                } else {
246
24
                    MP_LOG(loop, Log::Error)
247
24
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
24
                    throw std::runtime_error("invalid thread handle");
249
24
                }
250
24
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
24
                MP_LOG(loop, Log::Info)
282
24
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
24
                return kj::mv(e);
284
24
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
24
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
24
    }
292
24
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::rpc_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::rpc_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>>, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Rpc>, capnp::CallContext<ipc::capnp::messages::Rpc::ExecuteRpcParams, ipc::capnp::messages::Rpc::ExecuteRpcResults>>&, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Request, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::Uri, 17>, mp::ServerField<1, mp::Accessor<mp::rpc_fields::User, 17>, mp::ServerRet<mp::Accessor<mp::rpc_fields::Result, 18>, mp::ServerCall>>>> const&, mp::TypeList<UniValue, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>&&)
Line
Count
Source
63
4
{
64
4
    auto& server = server_context.proxy_server;
65
4
    EventLoop& loop = *server.m_context.loop;
66
4
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
4
    auto self = server.thisCap();
74
4
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
4
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
4
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
4
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
4
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
4
        auto& thread_context = g_thread_context;
97
4
        auto& request_threads = thread_context.request_threads;
98
4
        ConnThread request_thread;
99
4
        bool inserted{false};
100
4
        Mutex cancel_mutex;
101
4
        Lock cancel_lock{cancel_mutex};
102
4
        server_context.cancel_lock = &cancel_lock;
103
4
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
4
            if (cancel_monitor.m_canceled) {
106
4
                server_context.request_canceled = true;
107
4
                return;
108
4
            }
109
            // Detect request being canceled while it executes.
110
4
            assert(!cancel_monitor.m_on_cancel);
111
4
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
4
                Lock cancel_lock{cancel_mutex};
125
4
                server_context.request_canceled = true;
126
4
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
4
            std::tie(request_thread, inserted) = SetThread(
132
4
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
4
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
4
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
4
        const bool erase_thread{inserted};
142
4
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
4
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
4
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
4
                cancel_monitor.m_on_cancel = nullptr;
166
4
                auto self_dispose{kj::mv(self)};
167
4
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
4
                    ConnThreads::node_type removed;
175
4
                    {
176
4
                        Lock lock(thread_context.waiter->m_mutex);
177
4
                        removed = request_threads.extract(server.m_context.connection);
178
4
                    }
179
4
                }
180
4
            });
181
4
        );
182
4
        if (server_context.request_canceled) {
183
4
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
4
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
4
            try {
186
4
                fn.invoke(server_context, args...);
187
4
            } catch (const InterruptException& e) {
188
4
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
4
            }
190
4
        })) {
191
4
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
4
            kj::throwRecoverableException(kj::mv(*exception));
193
4
        }
194
4
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
4
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
4
    const auto& params = server_context.call_context.getParams();
202
4
    Context::Reader context_arg = Accessor::get(params);
203
4
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
4
    } else {
234
4
        auto thread_client = context_arg.getThread();
235
4
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
4
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
4
                KJ_IF_MAYBE (thread_server, perhaps) {
241
4
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
4
                    MP_LOG(loop, Log::Debug)
243
4
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
4
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
4
                } else {
246
4
                    MP_LOG(loop, Log::Error)
247
4
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
4
                    throw std::runtime_error("invalid thread handle");
249
4
                }
250
4
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
4
                MP_LOG(loop, Log::Info)
282
4
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
4
                return kj::mv(e);
284
4
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
4
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
4
    }
292
4
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>, mp::ServerDestroy, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::DestroyParams, ipc::capnp::messages::Echo::DestroyResults>>&, mp::ServerDestroy const&, mp::TypeList<>&&)
Line
Count
Source
63
6
{
64
6
    auto& server = server_context.proxy_server;
65
6
    EventLoop& loop = *server.m_context.loop;
66
6
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
6
    auto self = server.thisCap();
74
6
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
6
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
6
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
6
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
6
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
6
        auto& thread_context = g_thread_context;
97
6
        auto& request_threads = thread_context.request_threads;
98
6
        ConnThread request_thread;
99
6
        bool inserted{false};
100
6
        Mutex cancel_mutex;
101
6
        Lock cancel_lock{cancel_mutex};
102
6
        server_context.cancel_lock = &cancel_lock;
103
6
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
6
            if (cancel_monitor.m_canceled) {
106
6
                server_context.request_canceled = true;
107
6
                return;
108
6
            }
109
            // Detect request being canceled while it executes.
110
6
            assert(!cancel_monitor.m_on_cancel);
111
6
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
6
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
6
                Lock cancel_lock{cancel_mutex};
125
6
                server_context.request_canceled = true;
126
6
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
6
            std::tie(request_thread, inserted) = SetThread(
132
6
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
6
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
6
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
6
        const bool erase_thread{inserted};
142
6
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
6
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
6
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
6
                cancel_monitor.m_on_cancel = nullptr;
166
6
                auto self_dispose{kj::mv(self)};
167
6
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
6
                    ConnThreads::node_type removed;
175
6
                    {
176
6
                        Lock lock(thread_context.waiter->m_mutex);
177
6
                        removed = request_threads.extract(server.m_context.connection);
178
6
                    }
179
6
                }
180
6
            });
181
6
        );
182
6
        if (server_context.request_canceled) {
183
6
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
6
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
6
            try {
186
6
                fn.invoke(server_context, args...);
187
6
            } catch (const InterruptException& e) {
188
6
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
6
            }
190
6
        })) {
191
6
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
6
            kj::throwRecoverableException(kj::mv(*exception));
193
6
        }
194
6
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
6
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
6
    const auto& params = server_context.call_context.getParams();
202
6
    Context::Reader context_arg = Accessor::get(params);
203
6
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
6
    } else {
234
6
        auto thread_client = context_arg.getThread();
235
6
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
6
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
6
                KJ_IF_MAYBE (thread_server, perhaps) {
241
6
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
6
                    MP_LOG(loop, Log::Debug)
243
6
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
6
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
6
                } else {
246
6
                    MP_LOG(loop, Log::Error)
247
6
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
6
                    throw std::runtime_error("invalid thread handle");
249
6
                }
250
6
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
6
                MP_LOG(loop, Log::Info)
282
6
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
6
                return kj::mv(e);
284
6
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
6
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
6
    }
292
6
}
std::enable_if<std::is_same<decltype(mp::Accessor<mp::echo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::echo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::Echo>, capnp::CallContext<ipc::capnp::messages::Echo::EchoParams, ipc::capnp::messages::Echo::EchoResults>>&, mp::ServerField<1, mp::Accessor<mp::echo_fields::Echo, 17>, mp::ServerRet<mp::Accessor<mp::echo_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&>&&)
Line
Count
Source
63
9
{
64
9
    auto& server = server_context.proxy_server;
65
9
    EventLoop& loop = *server.m_context.loop;
66
9
    int req = server_context.req;
67
    // Keep a reference to the ProxyServer instance by assigning it to the self
68
    // variable. ProxyServer instances are reference-counted and if the client
69
    // drops its reference and the IPC call is canceled, this variable keeps the
70
    // instance alive until the method finishes executing. The self variable
71
    // needs to be destroyed on the event loop thread so it is freed in a sync()
72
    // call below.
73
9
    auto self = server.thisCap();
74
9
    auto invoke = [self = kj::mv(self), call_context = kj::mv(server_context.call_context), &server, &loop, req, fn, args...](CancelMonitor& cancel_monitor) mutable {
75
9
        MP_LOG(loop, Log::Debug) << "IPC server executing request #" << req;
76
9
        if (loop.testing_hook_async_request_start) loop.testing_hook_async_request_start();
77
9
        KJ_DEFER(if (loop.testing_hook_async_request_done) loop.testing_hook_async_request_done());
78
9
        ServerContext server_context{server, call_context, req};
79
        // Before invoking the function, store a reference to the
80
        // callbackThread provided by the client in the
81
        // thread_local.request_threads map. This way, if this
82
        // server thread needs to execute any RPCs that call back to
83
        // the client, they will happen on the same client thread
84
        // that is waiting for this function, just like what would
85
        // happen if this were a normal function call made on the
86
        // local stack.
87
        //
88
        // If the request_threads map already has an entry for this
89
        // connection, it will be left unchanged, and it indicates
90
        // that the current thread is an RPC client thread which is
91
        // in the middle of an RPC call, and the current RPC call is
92
        // a nested call from the remote thread handling that RPC
93
        // call. In this case, the callbackThread value should point
94
        // to the same thread already in the map, so there is no
95
        // need to update the map.
96
9
        auto& thread_context = g_thread_context;
97
9
        auto& request_threads = thread_context.request_threads;
98
9
        ConnThread request_thread;
99
9
        bool inserted{false};
100
9
        Mutex cancel_mutex;
101
9
        Lock cancel_lock{cancel_mutex};
102
9
        server_context.cancel_lock = &cancel_lock;
103
9
        loop.sync([&] {
104
            // Detect request being canceled before it executes.
105
9
            if (cancel_monitor.m_canceled) {
106
9
                server_context.request_canceled = true;
107
9
                return;
108
9
            }
109
            // Detect request being canceled while it executes.
110
9
            assert(!cancel_monitor.m_on_cancel);
111
9
            cancel_monitor.m_on_cancel = [&loop, &server_context, &cancel_mutex, req]() {
112
9
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled while executing.";
113
                // Lock cancel_mutex here to block the event loop
114
                // thread and prevent it from deleting the request's
115
                // params and response structs while the execution
116
                // thread is accessing them. Because this lock is
117
                // released before the event loop thread does delete
118
                // the structs, the mutex does not provide any
119
                // protection from the event loop deleting the
120
                // structs _before_ the execution thread acquires
121
                // it. So in addition to locking the mutex, the
122
                // execution thread always checks request_canceled
123
                // as well before accessing the structs.
124
9
                Lock cancel_lock{cancel_mutex};
125
9
                server_context.request_canceled = true;
126
9
            };
127
            // Update requests_threads map if not canceled. We know
128
            // the request is not canceled currently because
129
            // cancel_monitor.m_canceled was checked above and this
130
            // code is running on the event loop thread.
131
9
            std::tie(request_thread, inserted) = SetThread(
132
9
                GuardedRef{thread_context.waiter->m_mutex, request_threads}, server.m_context.connection,
133
9
                [&] { return Accessor::get(call_context.getParams()).getCallbackThread(); });
134
9
        });
135
136
        // If an entry was inserted into the request_threads map,
137
        // remove it after calling fn.invoke. If an entry was not
138
        // inserted, one already existed, meaning this must be a
139
        // recursive call (IPC call calling back to the caller which
140
        // makes another IPC call), so avoid modifying the map.
141
9
        const bool erase_thread{inserted};
142
9
        KJ_DEFER(
143
            // Release the cancel lock before calling loop->sync and
144
            // waiting for the event loop thread, because if a
145
            // cancellation happened, it needs to run the on_cancel
146
            // callback above. It's safe to release cancel_lock at
147
            // this point because the fn.invoke() call below will be
148
            // finished and no longer accessing the params or
149
            // results structs.
150
9
            cancel_lock.m_lock.unlock();
151
            // Erase the request_threads entry on the event loop
152
            // thread with loop->sync(), so if the connection is
153
            // broken there is not a race between this thread and
154
            // the disconnect handler trying to destroy the thread
155
            // client object.
156
9
            loop.sync([&] {
157
                // Clear cancellation callback. At this point the
158
                // method invocation finished and the result is
159
                // either being returned, or discarded if a
160
                // cancellation happened. So we do not need to be
161
                // notified of cancellations after this point. Also
162
                // we do not want to be notified because
163
                // cancel_mutex and server_context could be out of
164
                // scope when it happens.
165
9
                cancel_monitor.m_on_cancel = nullptr;
166
9
                auto self_dispose{kj::mv(self)};
167
9
                if (erase_thread) {
168
                    // Look up the thread again without using existing
169
                    // iterator since entry may no longer be there after
170
                    // a disconnect. Destroy node after releasing
171
                    // Waiter::m_mutex, so the ProxyClient<Thread>
172
                    // destructor is able to use EventLoop::mutex
173
                    // without violating lock order.
174
9
                    ConnThreads::node_type removed;
175
9
                    {
176
9
                        Lock lock(thread_context.waiter->m_mutex);
177
9
                        removed = request_threads.extract(server.m_context.connection);
178
9
                    }
179
9
                }
180
9
            });
181
9
        );
182
9
        if (server_context.request_canceled) {
183
9
            MP_LOG(loop, Log::Info) << "IPC server request #" << req << " canceled before it could be executed";
184
9
        } else KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]{
185
9
            try {
186
9
                fn.invoke(server_context, args...);
187
9
            } catch (const InterruptException& e) {
188
9
                MP_LOG(loop, Log::Info) << "IPC server request #" << req << " interrupted (" << e.what() << ")";
189
9
            }
190
9
        })) {
191
9
            MP_LOG(loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
192
9
            kj::throwRecoverableException(kj::mv(*exception));
193
9
        }
194
9
        return call_context;
195
        // End of scope: if KJ_DEFER was reached, it runs here
196
9
    };
197
198
    // Lookup Thread object specified by the client. The specified thread should
199
    // be a local Thread::Server object, but it needs to be looked up
200
    // asynchronously with getLocalServer().
201
9
    const auto& params = server_context.call_context.getParams();
202
9
    Context::Reader context_arg = Accessor::get(params);
203
9
    if (!context_arg.hasThread()) {
204
        // No client thread specified — dispatch through the server thread
205
        // pool, picking the slot with the smallest in-flight depth.
206
0
        auto* connection = server.m_context.connection;
207
0
        auto& pool = connection->m_thread_pool;
208
0
        if (pool.empty()) {
209
0
            MP_LOG(loop, Log::Error)
210
0
                << "IPC server error request #" << req << ", no thread specified and no pool configured";
211
0
            throw std::runtime_error("no thread specified and no pool configured");
212
0
        }
213
0
        auto* slot = &pool[0];
214
0
        for (size_t i = 1; i < pool.size(); ++i) {
215
0
            if (pool[i].depth < slot->depth) slot = &pool[i];
216
0
        }
217
0
        ++slot->depth;
218
0
        auto result = connection->m_threads.getLocalServer(slot->client)
219
0
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable -> kj::Promise<typename ServerContext::CallContext> {
220
0
                KJ_IF_MAYBE (thread_server, perhaps) {
221
0
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
222
0
                    MP_LOG(loop, Log::Debug)
223
0
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
224
0
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
225
0
                } else {
226
0
                    MP_LOG(loop, Log::Error)
227
0
                        << "IPC server error request #" << req << ", pool thread not found";
228
0
                    throw std::runtime_error("pool thread not found");
229
0
                }
230
0
            })
231
0
            .attach(kj::defer([slot] { --slot->depth; }));
232
0
        return connection->m_canceler.wrap(kj::mv(result));
233
9
    } else {
234
9
        auto thread_client = context_arg.getThread();
235
9
        auto result = server.m_context.connection->m_threads.getLocalServer(thread_client)
236
9
            .then([&loop, invoke = kj::mv(invoke), req](const kj::Maybe<Thread::Server&>& perhaps) mutable {
237
                // Assuming the thread object is found, pass it a pointer to the
238
                // `invoke` lambda above which will invoke the function on that
239
                // thread.
240
9
                KJ_IF_MAYBE (thread_server, perhaps) {
241
9
                    auto& thread = static_cast<ProxyServer<Thread>&>(*thread_server);
242
9
                    MP_LOG(loop, Log::Debug)
243
9
                        << "IPC server post request  #" << req << " {" << thread.m_thread_context.thread_name << "}";
244
9
                    return thread.template post<typename ServerContext::CallContext>(std::move(invoke));
245
9
                } else {
246
9
                    MP_LOG(loop, Log::Error)
247
9
                        << "IPC server error request #" << req << ", missing thread to execute request";
248
9
                    throw std::runtime_error("invalid thread handle");
249
9
                }
250
9
            }, [&loop, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
251
                // If you see the error "(remote):0: failed: remote exception:
252
                // Called null capability" here, it probably means your Init class
253
                // is missing a declaration like:
254
                //
255
                //   construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
256
                //
257
                // which passes a ThreadMap reference from the client to the server,
258
                // allowing the server to create threads to run IPC calls on the
259
                // client, and also returns a ThreadMap reference from the server to
260
                // the client, allowing the client to create threads on the server.
261
                // (Typically the latter ThreadMap is used more often because there
262
                // are more client-to-server calls.)
263
                //
264
                // If the other side of the connection did not previously get a
265
                // ThreadMap reference from this side of the connection, when the
266
                // other side calls `m_thread_map.makeThreadRequest()` in
267
                // `BuildField` above, `m_thread_map` will be null, but that call
268
                // will not fail immediately due to Cap'n Proto's request pipelining
269
                // and delayed execution. Instead that call will return an invalid
270
                // Thread reference, and when that reference is passed to this side
271
                // of the connection as `thread_client` above, the
272
                // `getLocalServer(thread_client)` call there will be the first
273
                // thing to overtly fail, leading to an error here.
274
                //
275
                // Potentially there are also other things that could cause errors
276
                // here, but this is the most likely cause.
277
                //
278
                // The log statement here is not strictly necessary since the same
279
                // exception will also be logged in serverInvoke, but this logging
280
                // may provide extra context that could be helpful for debugging.
281
9
                MP_LOG(loop, Log::Info)
282
9
                    << "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
283
9
                return kj::mv(e);
284
9
            });
285
        // Use connection m_canceler object to cancel the result promise if the
286
        // connection is destroyed. (By default Cap'n Proto does not cancel requests
287
        // on disconnect, since it's possible clients might want to make requests
288
        // and immediately disconnect without waiting for results, but not want the
289
        // requests to be canceled.)
290
9
        return server.m_context.connection->m_canceler.wrap(kj::mv(result));
291
9
    }
292
9
}
293
} // namespace mp
294
295
#endif // MP_PROXY_TYPE_CONTEXT_H