Add support for running on windows. These changes make the libmultiprocess API more generic, using stream types instead of file descriptors. All features are supported, including spawning processes with socket connections to the parent process. These changes were originally made in https://github.com/bitcoin/bitcoin/pull/32387
Add windows support #231
pull ryanofsky wants to merge 13 commits into bitcoin-core:master from ryanofsky:pr/win changing 13 files +353 −89-
ryanofsky commented at 8:31 AM on October 22, 2025: collaborator
-
DrahtBot commented at 8:31 AM on October 22, 2025: none
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--021abf342d371248e50ceaed478a90ca-->
Reviews
See the guideline for information on the review process. A summary of reviews will appear here.
<!--174a7506f384e20aa4161008e828411d-->
Conflicts
Reviewers, this pull request conflicts with the following ones:
- #269 (proxy: add local connection limit to ListenConnections by enirox001)
- #218 (Better error and log messages by ryanofsky)
- #209 (cmake: Increase cmake policy version by ryanofsky)
- #175 (Set cmake_minimum_required(VERSION 3.22) by maflcko)
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
<!--5faf32d7da4f0f540f40219e4f7537a3-->
LLM Linter (✨ experimental)
Possible places where named args for integral literals may be used (e.g.
func(x, /*named_arg=*/0)in C++, andfunc(x, named_arg=0)in Python):send(m_socket, pos, static_cast<int>(kj::min(size, WRITE_CLAMP_SIZE)), 0)insrc/mp/proxy.cppCreateNamedPipeA(pipe_path.c_str(), PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_WAIT, 1, 0, 0, 0, nullptr)insrc/mp/util.cppCreateProcessA(nullptr, const_cast<char*>(cmd.c_str()), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi)insrc/mp/util.cppCreateFileA(connect_info.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr)insrc/mp/util.cppWSASocketA(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &info, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT)insrc/mp/util.cpp
<sup>2026-04-17 01:40:53</sup>
- DrahtBot added the label Needs rebase on Jan 13, 2026
- pavlenex referenced this in commit 9800329587 on Mar 24, 2026
- ryanofsky force-pushed on Apr 3, 2026
- DrahtBot removed the label Needs rebase on Apr 3, 2026
-
pavlenex commented at 8:58 AM on April 8, 2026: none
We have miners using Stratum V2 that are on Windows and would like to see Windows IPC support landing, is there any estimate when they can expect this to materialize? We have a dozen of users who can help with testing if that's a blocker.
-
Sjors commented at 10:20 AM on April 8, 2026: member
@pavlenex testing would definitely be useful. I guess for that to work we'd need a stack of pull requests: this one here in libmultiprocess, one in Bitcoin Core that enables IPC support in the Windows Guix build, and then an SRI pull request that uses it. Testers would then have download (or build themselves) the custom bitcoin core and SRI binaries.
I've added Windows support to my v32 wish list: https://github.com/bitcoin/bitcoin/issues/33777
No guarantees obviously.
-
ryanofsky commented at 1:18 PM on April 8, 2026: collaborator
I'll try to get this PR ready for review this week, split up into smaller commits and with ci passing. From there as Sjors mentioned there is a lot more work to do: more code changes in https://github.com/bitcoin/bitcoin/pull/32387 that need to be made in bitcoin core, enabling IPC into windows builds in bitcoin core, enabling it in windows CI jobs with pycapnp, adding client support, probably adding a windows CI job to this repository.
v32 sounds like a good target though and it is very useful to know there is demand for this feature, because it hasn't been a priority so far
-
doc: Bump version 11 > 12 540c759014
-
088bdafd42
util, refactor: Add ProcessId type alias and use it
Add ProcessId = int type alias and apply it to WaitProcess, SpawnProcess (pid output argument), and callers.
-
2c6eb8ebc3
util, refactor: Add SocketId type alias and use it
Add SocketId = int and SocketError = -1 type aliases and apply SocketId to SpawnProcess (return type and callback parameter) and callers.
-
4f5649d2c0
util, refactor: Add ConnectInfo type alias and use it
Add ConnectInfo type alias to pass socket handle from parent process to child process in more platform independent way.
-
6320da2e1e
proxy, refactor: Replace EventLoop wakeup fd integers with KJ stream objects
Replace the m_wait_fd/m_post_fd raw int members with m_wait_stream/m_post_stream kj::Own<kj::AsyncIoStream> and m_post_writer kj::Own<kj::OutputStream>. The constructor uses provider->newTwoWayPipe() instead of calling socketpair() directly. The loop() and post() methods write through m_post_writer instead of calling write() with a raw fd, and EventLoopRef::reset does the same.
- ryanofsky closed this on Apr 15, 2026
- ryanofsky force-pushed on Apr 15, 2026
- ryanofsky reopened this on Apr 15, 2026
-
ryanofsky commented at 4:42 PM on April 15, 2026: collaborator
PR is split up into commits now and should be reviewable. CI is not passing but failures look like IWYU errors. I also opened https://github.com/bitcoin/bitcoin/pull/35084 with corresponding bitcoin core changes. Windows support for bitcoin core can be tested with https://github.com/bitcoin/bitcoin/pull/32387 which combines both PRs and enables IPC by default in windows builds.
<!-- begin push-3 -->
Rebased 2975facdd1ddb3b52d052aaaac611566f3ff27ae -> cb16d2e37ece4ce9326f03624f7f195ae0e55462 (
pr/win.2->pr/win.3, compare)<!-- end --> splitting changes up into more reviewable commits<!-- begin push-4 -->
Updated cb16d2e37ece4ce9326f03624f7f195ae0e55462 -> e563c968b4e13ac54aaec474e2a6bd98cd0c1c36 (
pr/win.3->pr/win.4, compare)<!-- end --> to fix various ci failures #231 (comment): macos/freebsd shutdownwrite fail, bitcoin core ci jobs api incompatibility, iwyu and olddeps fixes<!-- begin push-5 -->
Updated e563c968b4e13ac54aaec474e2a6bd98cd0c1c36 -> d9fcac6e30d0b9ea2cf24814bb014be70e6a2ade (
pr/win.4->pr/win.5, compare)<!-- end --> to fix IWYU errors https://github.com/bitcoin-core/libmultiprocess/actions/runs/24539430990/job/71741856575?pr=231<!-- begin push-6 -->
Added 1 commits d9fcac6e30d0b9ea2cf24814bb014be70e6a2ade -> a1748e2fae3abf7ba41ffa78db82eae4761f6723 (
pr/win.5->pr/win.6, compare)<!-- end --> to fix bitcoin core macos exception type error https://github.com/bitcoin-core/libmultiprocess/actions/runs/24541950788/job/71749481354?pr=231 -
921591d16e
cmake: Bump minimum required Cap'n Proto version to 0.9
kj::AsyncIoStream::getFd() was added in capnproto 0.9 (commit d27bfb8a4175b32b783de68d93dd1dbafadddea5, first released in 0.9.0). The code now uses getFd() in proxy.cpp, so 0.7 is no longer a sufficient minimum. Set olddeps version to 0.9.2, which is the patched 0.9.x release for CVE-2022-46149. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-
9a0bf73d8f
util, refactor: Add SocketPair() and use it in SpawnProcess
Extract socket pair creation from SpawnProcess into a standalone SocketPair() function, and use it to replace the inline socketpair() call. No behavior change.
-
34b0b24584
util: Clear FD_CLOEXEC on child socket before exec
Explicitly clear FD_CLOEXEC on the child's socket before calling exec, so the fd survives into the spawned process regardless of how the socket was created. Previously this relied on socketpair() not setting FD_CLOEXEC by default, which is not guaranteed if the caller creates sockets with SOCK_CLOEXEC or if the flag gets set by other means.
-
aff37a9a64
proxy, refactor: Change ConnectStream and ServeStream to accept stream objects
Instead of accepting raw file descriptor integers and wrapping them internally, ConnectStream and ServeStream now accept kj::Own<kj::AsyncIoStream> directly. This removes the assumption that the transport is always a local unix fd, making the API easier to adapt to other I/O types (e.g. Windows handles). The Stream type alias (kj::Own<kj::AsyncIoStream>) is added as a convenience, along with StreamSocketId() to extract the underlying fd from a Stream when needed. Callers are updated to wrap their fd with wrapSocketFd() before calling.
-
43dfe34943
proxy: Call shutdownWrite() in Connection destructor
Flush pending Cap'n Proto release messages before closing the stream. When one side of a socket pair closes, the other side does not receive an onDisconnect event, so it relies on receiving release messages from the closing side to free its ProxyServer objects and shut down cleanly. Without this, Server objects are not freed by Cap'n Proto on disconnection.
-
53dc5b9dfc
util: Add Windows support
Add Windows-specific code to support building and running on Windows: - util.h: Guard ProcessId/SocketId/SocketError type aliases with WIN32 ifdefs so they use SOCKET/uintptr_t on Windows and int on Unix. Add winsock2.h include on Windows. - util.cpp: Guard Unix-specific system headers with WIN32 ifdefs. Add Windows-specific includes (windows.h, winsock2.h). Guard MaxFd() with #ifndef WIN32. Add GetCurrentThreadId() branch in ThreadName(). Add win32Socketpair() forward-declare. Add Windows branch in SocketPair() using win32Socketpair(). Add CommandLineFromArgv() helper needed to construct CreateProcess command lines. Add Windows branch in SpawnProcess() using named pipes and WSADuplicateSocket to pass socket to child. Add Windows branch in StartSpawned() reading socket from named pipe. Add Windows branch in WaitProcess() using WaitForSingleObject/GetExitCodeProcess. - proxy-io.h: Add Windows branch in StreamSocketId() using getWin32Handle(). - proxy.cpp: Add SocketOutputStream class on Windows (analogous to FdOutputStream but using SOCKET/send()). Add Windows branch in EventLoop constructor to create m_post_writer using SocketOutputStream.
-
d9fcac6e30
ci: check out bitcoin/bitcoin PR #35084 instead of master
This repo has introduced API changes to add Windows support to libmultiprocess (HANDLE-based IPC alongside the existing fd-based IPC). These changes require corresponding updates to Bitcoin Core, which are pending in bitcoin/bitcoin#35084. Until that PR merges, the Bitcoin Core CI jobs fail against master because Bitcoin Core has not yet been updated to use the new API. Switch the Bitcoin Core checkout in both jobs to use refs/pull/35084/merge so CI tests against the compatible version. A BITCOIN_CORE_REF env var is introduced at the top of the file; once (and keep the var in place for any future API compatibility cycles). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ryanofsky force-pushed on Apr 16, 2026
- ryanofsky force-pushed on Apr 17, 2026
-
a1748e2fae
proxy: Fix shutdownWrite() exception handling on macOS with dynamic libraries
On macOS, when libcapnp is built as a dynamic library and Bitcoin Core REDUCE_EXPORT option is used the RTTI typeinfo for kj::Exception has a different address in libcapnp.dylib versus the calling binary. This means catch (const kj::Exception& e) in the calling binary silently fails to match exceptions thrown by capnp, so the DISCONNECTED exception from shutdownWrite() propagates as a fatal uncaught exception instead of being suppressed as intended. This causes the Bitcoin Core macOS native CI job to fail with: Fatal uncaught kj::Exception: kj/async-io-unix.c++:491: disconnected: shutdown(fd, SHUT_WR): Socket is not connected The fix is to use kj::runCatchingExceptions/kj::throwRecoverableException, which use KJ's own thread-level exception interception mechanism rather than C++ RTTI-based matching, and therefore work correctly across dynamic library boundaries. This is the same approach used elsewhere in the codebase (proxy.cpp EventLoop::post, type-context.h server request handler) for the same reason. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -
Sjors commented at 1:57 PM on April 20, 2026: member
How realistic is it to add a Windows CI job here? (can be cross-compiled)