Skip to content

Add makePool method on ThreadMap#283

Open
rustaceanrob wants to merge 1 commit into
bitcoin-core:masterfrom
rustaceanrob:26-5-29-pooled-threads
Open

Add makePool method on ThreadMap#283
rustaceanrob wants to merge 1 commit into
bitcoin-core:masterfrom
rustaceanrob:26-5-29-pooled-threads

Conversation

@rustaceanrob
Copy link
Copy Markdown

@rustaceanrob rustaceanrob commented May 29, 2026

This patch introduces a pool of threads to the Connection class, and allows this pool to be populated with the thread map via makePool. When a client thread is not set in a request context, it is delegated to the pool. This is unable to handle the guarentees with server-invoked callbacks that the current API offers, but these callbacks are not yet present in the interface.

The pool is implemented as round-robin as it is simplest, but perhaps the pool could be a queue of requests with work-stealing for threads that are available.

This was raised to me by Rust users, as they did not particularly care where work is executed on the server-side, but they have to set the thread regardless.

Tests in Rust can be seen here: 2140-dev/bitcoin-capnp-types#24

ref: https://github.com/2140-dev/bitcoin-capnp-types/blob/master/tests/util/bitcoin_core.rs#L149
ref: #281

@DrahtBot
Copy link
Copy Markdown

DrahtBot commented May 29, 2026

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.
A summary of reviews will appear here.

@rustaceanrob rustaceanrob changed the title Add makePool method on ThreadMap WIP: Add makePool method on ThreadMap May 29, 2026
@rustaceanrob rustaceanrob force-pushed the 26-5-29-pooled-threads branch from d56e637 to 158f30d Compare May 29, 2026 17:20
@rustaceanrob rustaceanrob changed the title WIP: Add makePool method on ThreadMap Add makePool method on ThreadMap May 29, 2026
@rustaceanrob rustaceanrob marked this pull request as ready for review May 29, 2026 17:40
Comment thread include/mp/proxy.capnp Outdated
# Pre-allocate a pool of server threads for implicit dispatch. When a
# request arrives with no context.thread set, the server round-robins
# across the pool instead of returning an error.
makePool @1 (count :UInt32) -> ();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
makePool @1 (count :UInt32) -> ();
makePool @1 (name: Text, count :UInt32) -> ();

It would be usefull to have a name parameter like makeThread. Since each client may have its own thread pool, naming them would make log lines like "IPC server post request #{name} {pool/0}" attributable to a specific client

Copy link
Copy Markdown
Author

@rustaceanrob rustaceanrob May 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. How does that look?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I just have this nit to make it more consistent with makeThread:

         const std::string thread_name = pool_name + "/pool/" + std::to_string(i);
         std::promise<ThreadContext*> thread_context;
         std::thread thread([&loop, &thread_context, thread_name]() {
-            g_thread_context.thread_name = ThreadName(loop.m_exe_name) + " (" + thread_name + ")";
+            g_thread_context.thread_name = ThreadName(loop.m_exe_name) + " (from " + thread_name + ")";
             g_thread_context.waiter = std::make_unique<Waiter>();
             Lock lock(g_thread_context.waiter->m_mutex);
             thread_context.set_value(&g_thread_context);

Comment thread src/mp/proxy.cpp
Comment on lines +420 to +438
kj::Promise<void> ProxyServer<ThreadMap>::makePool(MakePoolContext context)
{
EventLoop& loop{*m_connection.m_loop};
const uint32_t count = context.getParams().getCount();
for (uint32_t i = 0; i < count; ++i) {
const std::string name = "pool/" + std::to_string(i);
std::promise<ThreadContext*> thread_context;
std::thread thread([&loop, &thread_context, name]() {
g_thread_context.thread_name = ThreadName(loop.m_exe_name) + " (" + name + ")";
g_thread_context.waiter = std::make_unique<Waiter>();
Lock lock(g_thread_context.waiter->m_mutex);
thread_context.set_value(&g_thread_context);
g_thread_context.waiter->wait(lock, [] { return !g_thread_context.waiter; });
});
auto thread_server = kj::heap<ProxyServer<Thread>>(m_connection, *thread_context.get_future().get(), std::move(thread));
m_connection.m_thread_pool.push_back(m_connection.m_threads.add(kj::mv(thread_server)));
}
return kj::READY_NOW;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeated calls silently grow the pool beyond the intended size, and since naming is based on the loop index starting from 0 each time, you'd end up with duplicate names like two threads both called pool/0 making logs ambiguous.

I'm not sure what would be the correct approach but I thought about:

  • Return an error if m_connection.m_thread_pool is not empty
  • Replace the old pool by cleaning the m_connection.m_thread_pool and create a fresh one with the new count. This makes sense if the client needs to resize its dedicated pool.

@rustaceanrob rustaceanrob force-pushed the 26-5-29-pooled-threads branch from 158f30d to 5808f62 Compare May 30, 2026 09:03
This patch introduces a pool of threads to the `Connection` class, and
allows this pool to be populated with the thread map via `makePool`.
When a client thread is not set in a request context, it is delegated to
the pool. This is unable to handle the guarentees with server-invoked
callbacks that the current API offers, but these callbacks are not yet
present in the interface.

The pool is implemented as round-robin as it is simplest, but perhaps
the pool could be a queue of requests with work-stealing for threads
that are available.

This was raised to me by Rust users, as they did not particularly care
where work is executed on the server-side, but they have to set the
thread regardless.

ref: https://github.com/2140-dev/bitcoin-capnp-types/blob/master/tests/util/bitcoin_core.rs#L149
@rustaceanrob rustaceanrob force-pushed the 26-5-29-pooled-threads branch from 5808f62 to d5bc804 Compare May 30, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants