Add windows support #231

pull ryanofsky wants to merge 10 commits into bitcoin-core:master from ryanofsky:pr/win changing 17 files +789 −147
  1. ryanofsky commented at 8:31 AM on October 22, 2025: collaborator

    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

  2. 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 and AI policy for information on the review process.

    Type Reviewers
    Approach ACK hebasto, ViniciusCestarii

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #333 (nix: Make clang tooling and IWYU find the right standard library headers by ryanofsky)
    • #212 (ci: add newdeps job testing newer versions of cmake and capnproto 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 typos and grammar issues:

    • It it possible -> It is possible [“It it” is a typo and breaks the sentence in include/mp/util.h.]

    <sup>2026-08-13 21:14:11</sup>

  3. DrahtBot added the label Needs rebase on Jan 13, 2026
  4. pavlenex referenced this in commit 9800329587 on Mar 24, 2026
  5. ryanofsky force-pushed on Apr 3, 2026
  6. ryanofsky commented at 9:43 PM on April 3, 2026: collaborator

    <!-- begin push-2 -->

    Rebased 7d2f3c4371f8689260061b4b98f0f15dff9d95f7 -> 2975facdd1ddb3b52d052aaaac611566f3ff27ae (pr/win.1 -> pr/win.2, compare)<!-- end --> due to conflicts with #237

  7. DrahtBot removed the label Needs rebase on Apr 3, 2026
  8. 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.

  9. 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.

  10. 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

  11. ryanofsky closed this on Apr 15, 2026

  12. ryanofsky force-pushed on Apr 15, 2026
  13. ryanofsky reopened this on Apr 15, 2026

  14. 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

    <!-- begin push-7 -->

    Updated a1748e2fae3abf7ba41ffa78db82eae4761f6723 -> 18fc18846e38dfec4b92a36b7a20d4f9be74a874 (pr/win.6 -> pr/win.7, compare)<!-- end --> with updates from https://github.com/bitcoin/bitcoin/pull/32387 pr/ipc-win.23

    <!-- begin push-8 -->

    Updated 18fc18846e38dfec4b92a36b7a20d4f9be74a874 -> 7fd5ec40bc8c2a1fa0e2645d2b587ce2c1c3d17d (pr/win.7 -> pr/win.8, compare)<!-- end --> adding workaround for ubuntu packaging bug exposed by cmake change https://github.com/bitcoin-core/libmultiprocess/actions/runs/24746487506/job/72399345861, also fixing olddeps include and iwyu errors https://github.com/bitcoin-core/libmultiprocess/actions/runs/24746487510, also fixing more MSVC errors, and rearranging commits

  15. ryanofsky force-pushed on Apr 16, 2026
  16. ryanofsky force-pushed on Apr 17, 2026
  17. 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)

  18. Sjors referenced this in commit d42af54c56 on Apr 20, 2026
  19. Sjors referenced this in commit 7de24f776c on Apr 20, 2026
  20. Sjors referenced this in commit 2f549eb17d on Apr 20, 2026
  21. ryanofsky commented at 5:21 PM on April 21, 2026: collaborator

    How realistic is it to add a Windows CI job here? (can be cross-compiled)

    I think this does need a windows CI job to exist in order to be merged, otherwise windows support is very likely to break with future changes, so thanks for opening #272.

    I think it may also make sense to split this PR up to separate the commits which are needed to support windows but don't actually add any windows code, from the one commit which actually does add windows code.

  22. Sjors commented at 7:00 PM on April 21, 2026: member

    it may also make sense to split this PR up to separate the commits which are needed to support windows but don't actually add any windows code

    Yes it would be good to land those changes to keep this PR focussed.

  23. ryanofsky force-pushed on Apr 21, 2026
  24. ryanofsky force-pushed on Apr 22, 2026
  25. Sjors referenced this in commit 8db8bf06ee on Apr 23, 2026
  26. Sjors referenced this in commit baf5d5e571 on Apr 23, 2026
  27. Sjors referenced this in commit 36b415c486 on Apr 23, 2026
  28. Sjors referenced this in commit a20bfd86d2 on Apr 23, 2026
  29. in include/mp/util.h:265 in 4f58c8c981
     260 | @@ -257,12 +261,20 @@ std::string LogEscape(const kj::StringTree& string, size_t max_size);
     261 |  
     262 |  using Stream = kj::Own<kj::AsyncIoStream>;
     263 |  
     264 | +#ifdef WIN32
     265 | +using ProcessId = uintptr_t;
    


    ViniciusCestarii commented at 5:50 PM on May 11, 2026:

    Out of curiosity, any reason ProcessId is uintptr_t rather than HANDLE on Windows? Would remove the reinterpret_casts


    ryanofsky commented at 1:26 PM on June 2, 2026:

    re: #231 (review)

    Out of curiosity, any reason ProcessId is uintptr_t rather than HANDLE on Windows? Would remove the reinterpret_casts

    Good question, I think you are right it could be good to use HANDLE instead. Probably original motivation for using uintptr_t was to avoid needing to pull in win32 headers for all code using mp/util.h, but maybe that bridge has already been crossed with inclusion of winsock2.h as per your other comment #231 (comment), so this should be revisited. Could also go the other direction and drop winsock2.h since it looks like it is only needed for INVALID_SOCKET definition.

    Another potentially confusing about this is that process handles and process ids (as returned by functions like GetCurrentProcessId) are actually different things on windows. Handles are more useful for controlling and waiting for processes, but process ids are more globally unique and useful in other contexts.

    Will look into alternatives here and try to add a clarifying comment at least.


    ryanofsky commented at 8:37 PM on July 29, 2026:

    re: #231 (review)

    This is switched to use HANDLE now

  30. ViniciusCestarii commented at 8:18 PM on May 11, 2026: contributor

    Tested mingw cross-build and native MSVC build

    Environment: Arch Linux (kernel 6.19), CMake 4.3.2, mingw-w64 GCC 15.2.0, Cap'n Proto 1.2.0, target Windows 11 (x64). MSVC side: MSBuild 18.5.4

    mingw cross (x86_64-w64-mingw32) worked for me with one small tweak. <winsock2.h> in util.h pulls winsock2.h -> ole2.h -> objbase.h -> rpc.h, which #defines interface as struct and breaks the build:

    #ifdef WIN32
    +#ifndef WIN32_LEAN_AND_MEAN
    +#define WIN32_LEAN_AND_MEAN
    +#endif
    #include <winsock2.h>
    #endif
    

    With that, build succeeds, examples run, and tests pass on Windows 11 10.0.26100

    MSVC native: same interface-as-struct macro pollution and more errors shows up but to make it work it seems it's not one line fix:

    <details> <summary>MSVC build log</summary> C:\Users\vinic\libmultiprocess>cmake --build build -j 1 MSBuild version 18.5.4+cb4e32d21 for .NET Framework

    util.cpp C:\Users\vinic\libmultiprocess\include\mp\util.h(187,38): warning C4003: not enough arguments for function-like macro i nvocation 'MP_ASSERT_CAPABILITY' [C:\Users\vinic\libmultiprocess\build\mputil.vcxproj] (compiling source file '../src/mp/util.cpp')

    C:\Users\vinic\libmultiprocess\src\mp\util.cpp(262,5): warning C4996: 'WSADuplicateSocketA': Use WSADuplicateSocketW() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings [C:\Users\vinic\libmultiprocess\bu ild\mputil.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\util.cpp(291,5): warning C4244: 'argument': conversion from 'SOCKET' to 'int', po ssible loss of data [C:\Users\vinic\libmultiprocess\build\mputil.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\util.cpp(290,19): warning C4996: 'WSASocketA': Use WSASocketW() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings [C:\Users\vinic\libmultiprocess\build\mputil.vcxpro j] mputil.vcxproj -> C:\Users\vinic\libmultiprocess\build\mputil.dir\Debug\mputil.lib gen.cpp C:\Users\vinic\libmultiprocess\include\mp\util.h(187,38): warning C4003: not enough arguments for function-like macro i nvocation 'MP_ASSERT_CAPABILITY' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] (compiling source file '../src/mp/gen.cpp')

    C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,66): error C2332: 'struct': missing tag name [C:\Users\vinic\libmultip rocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,66): error C2144: syntax error: '<unnamed-tag>' should be preceded by ')' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,66): error C2144: syntax error: '<unnamed-tag>' should be preceded by ';' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,66): error C2059: syntax error: ',' [C:\Users\vinic\libmultiprocess\bu ild\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,132): error C2332: 'struct': missing tag name [C:\Users\vinic\libmulti process\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,132): error C2144: syntax error: '<unnamed-tag>' should be preceded by ')' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,132): error C2062: type '' unexpected [C:\Users\vinic\libmultiprocess
    build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,171): error C2947: expecting '>' to terminate template-argument-list, found '>' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,182): error C2059: syntax error: ')' [C:\Users\vinic\libmultiprocess\b uild\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(81,1): error C2143: syntax error: missing ';' before '{' [C:\Users\vinic
    libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(81,1): error C2447: '{': missing function header (old-style formal list?) [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(392,35): error C2332: 'struct': missing tag name [C:\Users\vinic\libmulti process\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(392,35): error C2226: syntax error: unexpected type '<unnamed-tag>' [C:\U sers\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(414,36): error C2332: 'struct': missing tag name [C:\Users\vinic\libmulti process\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(414,36): error C2144: syntax error: '<unnamed-tag>' should be preceded by ')' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(414,13): error C2660: 'ForEachMethod': function does not take 0 arguments [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(80,13): see declaration of 'ForEachMethod' C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(414,13): while trying to match the argument list '()'

    C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(414,36): error C2144: syntax error: '<unnamed-tag>' should be preceded by ';' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(414,36): error C2059: syntax error: ',' [C:\Users\vinic\libmultiprocess\b uild\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(414,137): error C2143: syntax error: missing ';' before '{' [C:\Users\vin ic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(415,51): error C2065: 'method': undeclared identifier [C:\Users\vinic\lib multiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(417,35): error C2065: 'method': undeclared identifier [C:\Users\vinic\lib multiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(417,17): error C2672: 'GetAnnotationText': no matching overloaded functio n found [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(57,13): could be 'bool GetAnnotationText(const Reader &,uint64_t,kj::StringPtr *)'

    C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(419,92): error C2065: 'method_interface': undeclared identifier [C:\Users \vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(485,48): error C2065: 'method': undeclared identifier [C:\Users\vinic\lib multiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(485,33): error C2734: 'schema_field': 'const' object must be initialized if not 'extern' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(485,33): error C3531: 'schema_field': a symbol whose type contains 'auto' must have an initializer [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(485,46): error C2143: syntax error: missing ';' before ':' [C:\Users\vini c\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(485,81): error C2143: syntax error: missing ';' before ')' [C:\Users\vini c\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(488,48): error C2065: 'method': undeclared identifier [C:\Users\vinic\lib multiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(488,33): error C2734: 'schema_field': 'const' object must be initialized if not 'extern' [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(488,33): error C3531: 'schema_field': a symbol whose type contains 'auto' must have an initializer [C:\Users\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(488,46): error C2143: syntax error: missing ';' before ':' [C:\Users\vini c\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(488,82): error C2143: syntax error: missing ';' before ')' [C:\Users\vini c\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(504,55): error C2065: 'method_interface': undeclared identifier [C:\Users \vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(504,85): error C2332: 'struct': missing tag name [C:\Users\vinic\libmulti process\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(504,85): error C2226: syntax error: unexpected type '<unnamed-tag>' [C:\U sers\vinic\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(504,88): error C2143: syntax error: missing ';' before '{' [C:\Users\vini c\libmultiprocess\build\mpgen.vcxproj] C:\Users\vinic\libmultiprocess\src\mp\gen.cpp(620,14): error C2059: syntax error: ')' [C:\Users\vinic\libmultiprocess\b uild\mpgen.vcxproj] proxy.cpp C:\Users\vinic\libmultiprocess\include\mp\util.h(187,38): warning C4003: not enough arguments for function-like macro i nvocation 'MP_ASSERT_CAPABILITY' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\capability.h(88,40): error C2838: 'IPrintDialogServices': illegal qualified name in member declaration [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\capability.h(88,40): error C2275: 'IPrintDialogServices': expected an expression ins tead of a type [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\capability.h(88,40): error C2440: 'initializing': cannot convert from 'IPrintDialogS ervices' to 'const capnp::Kind' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\capability.h(88,40): No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

    C:\capnproto\install\include\capnp\capability.h(913,32): error C2838: 'IPrintDialogServices': illegal qualified name in member declaration [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\capability.h(913,32): error C2275: 'IPrintDialogServices': expected an expression in stead of a type [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\capability.h(913,32): prefix the qualified-id with 'typename' to indicate a type

    C:\capnproto\install\include\capnp\capability.h(913,32): error C2975: 'k': invalid template argument for 'capnp::_::Poi nterHelpers', expected compile-time constant expression [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(319,28): see declaration of 'k'

    C:\capnproto\install\include\capnp\capability.h(940,22): error C2838: 'IPrintDialogServices': illegal qualified name in member declaration [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\capability.h(940,22): error C2275: 'IPrintDialogServices': expected an expression in stead of a type [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\capability.h(940,22): prefix the qualified-id with 'typename' to indicate a type

    C:\capnproto\install\include\capnp\capability.h(940,22): error C2975: 'k': invalid template argument for 'capnp::List', expected compile-time constant expression [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(204,28): see declaration of 'k'

    C:\capnproto\install\include\capnp\capability.h(940,8): error C2953: 'capnp::List<T,capnp::Kind::PRIMITIVE>': class tem plate has already been defined [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\list.h(101,8): see declaration of 'capnp::List<T,capnp::Kind::PRIMITIVE>'

    C:\capnproto\install\include\capnp\capability.h(1285,43): error C2838: 'IPrintDialogServices': illegal qualified name i n member declaration [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\capability.h(1285,43): error C2275: 'IPrintDialogServices': expected an expression i nstead of a type [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\capability.h(1285,43): prefix the qualified-id with 'typename' to indicate a type

    C:\capnproto\install\include\capnp\capability.h(1285,43): error C2975: 'unnamed-parameter': invalid template argument f or 'capnp::Orphanage::GetInnerReader', expected compile-time constant expression [C:\Users\vinic\libmultiprocess\build
    multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\orphan.h(184,25): see declaration of 'unnamed-parameter'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(57,5): error C2838: 'IPrintDialogServices': illegal quali fied name in member declaration [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(57,5): error C2275: 'IPrintDialogServices': expected an e xpression instead of a type [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(57,5): error C2440: 'initializing': cannot convert from ' IPrintDialogServices' to 'const capnp::Kind' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(57,5): No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(106,5): error C2838: 'IPrintDialogServices': illegal qual ified name in member declaration [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(106,5): error C2275: 'IPrintDialogServices': expected an expression instead of a type [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(106,5): error C2440: 'initializing': cannot convert from 'IPrintDialogServices' to 'const capnp::Kind' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(106,5): No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(755,53): error C2039: 'get': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(755,53): error C3861: 'get': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(759,53): error C2039: 'get': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(759,53): error C2672: 'get': no matching overloaded funct ion found [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\kj\tuple.h(360,13): could be 'unknown-type kj::get(Tuple &&)' C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(759,53): 'unknown-type kj::get(Tuple &&)': could not deduce template argument for 'index'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(766,46): error C2039: 'set': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(766,46): error C3861: 'set': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(770,46): error C2039: 'set': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(770,46): error C3861: 'set': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(775,46): error C2039: 'adopt': is not a member of 'capnp: :::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(775,46): error C3861: 'adopt': identifier not found [C:\U sers\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(779,53): error C2039: 'disown': is not a member of 'capnp ::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(779,53): error C3861: 'disown': identifier not found [C:
    Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(852,53): error C2039: 'get': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(852,53): error C3861: 'get': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(856,53): error C2039: 'get': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(856,53): error C2672: 'get': no matching overloaded funct ion found [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\kj\tuple.h(360,13): could be 'unknown-type kj::get(Tuple &&)' C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(856,53): 'unknown-type kj::get(Tuple &&)': could not deduce template argument for 'index'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(863,46): error C2039: 'set': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(863,46): error C3861: 'set': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(867,46): error C2039: 'set': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(867,46): error C3861: 'set': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(872,46): error C2039: 'adopt': is not a member of 'capnp: :::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(872,46): error C3861: 'adopt': identifier not found [C:\U sers\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(876,53): error C2039: 'disown': is not a member of 'capnp ::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(876,53): error C3861: 'disown': identifier not found [C:
    Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(891,53): error C2039: 'get': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(891,53): error C3861: 'get': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(895,53): error C2039: 'get': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(895,53): error C2672: 'get': no matching overloaded funct ion found [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\kj\tuple.h(360,13): could be 'unknown-type kj::get(Tuple &&)' C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(895,53): 'unknown-type kj::get(Tuple &&)': could not deduce template argument for 'index'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(902,46): error C2039: 'set': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(902,46): error C3861: 'set': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(906,46): error C2039: 'set': is not a member of 'capnp::_ ::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::_::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(906,46): error C3861: 'set': identifier not found [C:\Use rs\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(911,46): error C2039: 'adopt': is not a member of 'capnp: :::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(911,46): error C3861: 'adopt': identifier not found [C:\U sers\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(915,53): error C2039: 'disown': is not a member of 'capnp ::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelpersmp::Thread,capnp::Kind::INTERFACE'

    C:\Users\vinic\libmultiprocess\build\include\mp\proxy.capnp.h(915,53): error C3861: 'disown': identifier not found [C:
    Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(724,60): error C2039: 'get': is not a member of 'capnp::::Poin terHelperscapnp::Capability,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelperscapnp::Capability,capnp::Kind::INTERFACE'

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(724,60): error C3861: 'get': identifier not found [C:\Users\vin ic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(728,60): error C2039: 'get': is not a member of 'capnp::::Poin terHelperscapnp::Capability,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelperscapnp::Capability,capnp::Kind::INTERFACE'

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(728,60): error C2672: 'get': no matching overloaded function fo und [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\kj\tuple.h(360,13): could be 'unknown-type kj::get(Tuple &&)' C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(728,60): 'unknown-type kj::get(Tuple &&)': could not deduce template argument for 'index'

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(735,53): error C2039: 'set': is not a member of 'capnp::::Poin terHelperscapnp::Capability,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelperscapnp::Capability,capnp::Kind::INTERFACE'

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(735,53): error C3861: 'set': identifier not found [C:\Users\vin ic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(739,53): error C2039: 'set': is not a member of 'capnp::::Poin terHelperscapnp::Capability,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelperscapnp::Capability,capnp::Kind::INTERFACE'

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(739,53): error C3861: 'set': identifier not found [C:\Users\vin ic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(744,53): error C2039: 'adopt': is not a member of 'capnp::::Po interHelperscapnp::Capability,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelperscapnp::Capability,capnp::Kind::INTERFACE'

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(744,53): error C3861: 'adopt': identifier not found [C:\Users\v inic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(748,60): error C2039: 'disown': is not a member of 'capnp::::P ointerHelperscapnp::Capability,capnp::Kind::INTERFACE' [C:\Users\vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp') C:\capnproto\install\include\capnp\common.h(320,8): see declaration of 'capnp::::PointerHelperscapnp::Capability,capnp::Kind::INTERFACE'

    C:\capnproto\install\include\capnp\rpc-twoparty.capnp.h(748,60): error C3861: 'disown': identifier not found [C:\Users
    vinic\libmultiprocess\build\multiprocess.vcxproj] (compiling source file '../src/mp/proxy.cpp')

    </details>

  31. ryanofsky commented at 1:27 PM on June 2, 2026: collaborator

    Thanks for the review! Will follow up and look into ProcessId and winsock2.h issues

  32. DrahtBot added the label Needs rebase on Jun 2, 2026
  33. ryanofsky referenced this in commit 3f221b5bfd on Jul 17, 2026
  34. ryanofsky force-pushed on Jul 17, 2026
  35. ryanofsky commented at 2:06 PM on July 17, 2026: collaborator

    This PR is rebased after the #274 merge, so should be ready for review now. It does look like fixes will be needed for CI, though, and I'm working on that.

    Note my goal with this PR is just to add a basic level of windows support so libmultiprocess cross-compiles with mingw and spawn and listen tests run in wine. More followups will probably be needed after this to fix any problems that arise with MSVC support or enabling IPC in windows builds for Bitcoin core. The full PR targeting all of that is https://github.com/bitcoin/bitcoin/pull/32387 and has some unresolved issues still.

    The downstream PR https://github.com/bitcoin/bitcoin/pull/35084 updating Bitcoin core to use the newer libmultiprocess API is also ready for review and can be reviewed in parallel to this.


    <!-- begin push-9 -->

    Rebased 7fd5ec40bc8c2a1fa0e2645d2b587ce2c1c3d17d -> 259f0101b61dbce11cf8c3b1ee678cc1719ead16 (pr/win.8 -> pr/win.9, compare)<!-- end --> after #274

    <!-- begin push-10 -->

    Updated 259f0101b61dbce11cf8c3b1ee678cc1719ead16 -> c0bc409b645844afc8da0feb609aa3ded41864e8 (pr/win.9 -> pr/win.10, compare)<!-- end --> making listen_tests fixes and enabling wine CI job

    <!-- begin push-11 -->

    Updated c0bc409b645844afc8da0feb609aa3ded41864e8 -> 5a707dccf145cf88587d10a42d524eff8af2041a (pr/win.10 -> pr/win.11, compare)<!-- end --> to fix windows CI nix errors https://github.com/bitcoin-core/libmultiprocess/actions/runs/29765222110/job/88429702800?pr=231

  36. DrahtBot removed the label Needs rebase on Jul 17, 2026
  37. ryanofsky force-pushed on Jul 20, 2026
  38. ryanofsky force-pushed on Jul 20, 2026
  39. in include/mp/util.h:282 in 37a2190728 outdated
     276 | @@ -277,22 +277,19 @@ using ProcessId = int;
     277 |  using SocketId = int;
     278 |  constexpr SocketId SocketError{-1};
     279 |  
     280 | -//! Information about parent process passed to child process as a command-line
     281 | -//! argument. On unix this is the child socket fd number formatted as a string.
     282 | -using SpawnConnectInfo = std::string;
    


    Sjors commented at 11:05 AM on July 21, 2026:

    In 37a2190728ecfb7e19b9c7641f645e8152f6ab1c Improve SpawnProcess API and documentation nit: could document your rationale for this (https://github.com/bitcoin-core/libmultiprocess/pull/274#discussion_r3602499719) in the commit message:

    The type SpawnConnectInfo and SpawnConnectInfoToArgsFn aliases introduced in beaa50a046190277a91cdf9ca87c169fd2516c55 hurt readability more than they help.


    ryanofsky commented at 4:51 PM on July 22, 2026:

    re: #231 (review)

    Thanks! Added more context

  40. in include/mp/util.h:292 in 37a2190728 outdated
     302 | +std::tuple<ProcessId, SocketId> SpawnProcess(const std::function<std::vector<std::string>(std::string)>& spawn_argv);
     303 | +
     304 | +//! Initialize spawned child process using the connection string passed by
     305 | +//! SpawnProcess through its command line arguments. Returns socket id for
     306 | +//! communicating with the parent process.
     307 | +SocketId StartSpawned(const std::string& connect_info);
    


    Sjors commented at 11:15 AM on July 21, 2026:

    In 37a2190728ecfb7e19b9c7641f645e8152f6ab1c Improve SpawnProcess API and documentation: would be good to specifically document the connect_info argument.

    //! Initialize spawned child process. The connect_info argument is the
    //! connection string SpawnProcess generated in the parent process and passed
    //! to the child on its command line. Returns socket id for communicating with
    //! the parent process.
    

    ryanofsky commented at 4:52 PM on July 22, 2026:

    re: #231 (review)

    Nice, applied this change

  41. in include/mp/util.h:286 in 37a2190728
     296 | +//! pair. Calls spawn_argv callback with a connection string that needs to be
     297 | +//! passed to the child process, and executes the argv command line it returns.
     298 | +//! Returns child process id and socket id.
     299 | +//!
     300 | +//! The connection string is just a file descriptor number on unix, and the
     301 | +//! child process can call SpawnProcess to parse it.
    


    Sjors commented at 2:22 PM on July 21, 2026:

    In 37a2190728ecfb7e19b9c7641f645e8152f6ab1c Improve SpawnProcess API and documentation: did you mean StartSpawned instead of SpawnProcess?

    //! The connection string is just a file descriptor number on unix, and the 
    //! child process can call StartSpawned to get a socket handle from the connection string.
    

    For other reviewers, here's a diagram of how StartSpawned, SpawnProcess and the spawn_argv dance, based on the calculator example:

    <img width="619" height="904" alt="Image" src="https://github.com/user-attachments/assets/49457df1-8813-48fc-aff0-949f315f9fac" />


    ryanofsky commented at 5:01 PM on July 22, 2026:

    re: #231 (review)

    Good catch, fixed StartSpawned references. SpawnProcess and StartSpawned complement each other with SpawnProcess being called by parent and returing socket id, and StartSpawned being called by child and returning socket id.

    ConnectStream and ServeStream are also complements of each other with ServeStream wrapping a C++ interface pointer and serving it over a stream, and ConnectStream wrapping a stream and returning a C++ interface pointer.

    Usually ConnectStream is paired with SpawnProcess in the parent, and ServeStream is paired with StartSpawned in the child because the parent wants to call C++ methods on some interface provided by the child. But it's also possible to do the reverse and have the parent serve an interface to the child, or to not use Connect and Serve at all and just communicate directly over the socketpair.

  42. Sjors commented at 3:20 PM on July 21, 2026: member

    Some feedback on the first commit.

  43. ryanofsky force-pushed on Jul 22, 2026
  44. ryanofsky commented at 5:05 PM on July 22, 2026: collaborator

    Thanks for the review! Am planning to update downstream PR https://github.com/bitcoin/bitcoin/pull/32387 shortly with these changes and other fixes


    <!-- begin push-12 -->

    Updated 5a707dccf145cf88587d10a42d524eff8af2041a -> 8a682fea6ad99730f718398347c556649a5bbaf1 (pr/win.11 -> pr/win.12, compare)<!-- end --> with suggested changes and another listen_tests fix

  45. in src/mp/util.cpp:140 in 86aa0296b7 outdated
     132 | @@ -117,10 +133,56 @@ std::string LogEscape(const kj::StringTree& string, size_t max_size)
     133 |      return result;
     134 |  }
     135 |  
     136 | +//! Generate command line that the executable being invoked will split up using
     137 | +//! the CommandLineToArgvW function, which expects arguments with spaces to be
     138 | +//! quoted, quote characters to be backslash-escaped, and backslashes to also be
     139 | +//! backslash-escaped, but only if they precede a quote character.
     140 | +std::string CommandLineFromArgv(const std::vector<std::string>& argv)
    


    Sjors commented at 2:52 PM on July 23, 2026:

    In 86aa0296b7ff93e9810e8863dd863a050d17e1b3 util: Add Windows support: maybe introduce this helper in a separate commit along with some tests. Claude also thinks it found a bug:

    diff --git a/include/mp/util.h b/include/mp/util.h
    index 1182479796..1a3b92eea2 100644
    --- a/include/mp/util.h
    +++ b/include/mp/util.h
    @@ -283,4 +283,11 @@ std::string ThreadName(const char* exe_name);
     std::string LogEscape(const kj::StringTree& string, size_t max_size);
    
    +//! Convert an argument vector into a single command line string suitable for
    +//! CreateProcess, following the quoting rules of CommandLineToArgvW, which
    +//! executables use to split the command line back into arguments. Declared
    +//! unconditionally (not just on windows) so it can be unit tested on any
    +//! platform.
    +std::string CommandLineFromArgv(const std::vector<std::string>& argv);
    +
     using Stream = kj::Own<kj::AsyncIoStream>;
    
    diff --git a/src/mp/util.cpp b/src/mp/util.cpp
    index 024840db0e..0b080765a7 100644
    --- a/src/mp/util.cpp
    +++ b/src/mp/util.cpp
    @@ -162,4 +162,9 @@ std::string CommandLineFromArgv(const std::vector<std::string>& argv)
                             out.append(backslash_count * 2 + 1, '\\');
                             out.push_back('"');
    +                    } else if (i == arg.size()) {
    +                        // Backslashes at the end of the argument precede the
    +                        // closing quote added below, so also need to be doubled
    +                        out.append(backslash_count * 2, '\\');
    +                        --i; // Compensate for the outer loop's increment
                         } else {
                             // Otherwise, backslashes remain as-is
    diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
    index 1324629392..c3c181af82 100644
    --- a/test/CMakeLists.txt
    +++ b/test/CMakeLists.txt
    @@ -30,4 +30,5 @@ if(BUILD_TESTING AND TARGET CapnProto::kj-test)
         mp/test/spawn_tests.cpp
         mp/test/test.cpp
    +    mp/test/util_tests.cpp
       )
       include(${PROJECT_SOURCE_DIR}/cmake/TargetCapnpSources.cmake)
    diff --git a/test/mp/test/util_tests.cpp b/test/mp/test/util_tests.cpp
    new file mode 100644
    index 0000000000..9f78042f16
    --- /dev/null
    +++ b/test/mp/test/util_tests.cpp
    @@ -0,0 +1,106 @@
    +// Copyright (c) The Bitcoin Core developers
    +// Distributed under the MIT software license, see the accompanying
    +// file COPYING or http://www.opensource.org/licenses/mit-license.php.
    +
    +#include <mp/util.h>
    +
    +#include <kj/test.h>
    +
    +#ifdef WIN32
    +#include <shellapi.h>
    +#include <string>
    +#include <vector>
    +#endif
    +
    +namespace mp {
    +namespace test {
    +
    +KJ_TEST("CommandLineFromArgv quoting")
    +{
    +    // Arguments without spaces, tabs, or quotes pass through unquoted, even if
    +    // they contain backslashes.
    +    KJ_EXPECT(CommandLineFromArgv({}) == "");
    +    KJ_EXPECT(CommandLineFromArgv({"simple"}) == "simple");
    +    KJ_EXPECT(CommandLineFromArgv({"a", "b", "c"}) == "a b c");
    +    KJ_EXPECT(CommandLineFromArgv({R"(C:\a\b)"}) == R"(C:\a\b)");
    +    KJ_EXPECT(CommandLineFromArgv({R"(\\.\pipe\mp-1234-1)"}) == R"(\\.\pipe\mp-1234-1)");
    +    KJ_EXPECT(CommandLineFromArgv({R"(\)"}) == R"(\)");
    +
    +    // Empty arguments must be quoted so they are not dropped.
    +    KJ_EXPECT(CommandLineFromArgv({""}) == R"("")");
    +    KJ_EXPECT(CommandLineFromArgv({"a", "", "b"}) == R"(a "" b)");
    +
    +    // Arguments with spaces or tabs are quoted.
    +    KJ_EXPECT(CommandLineFromArgv({"has space"}) == R"("has space")");
    +    KJ_EXPECT(CommandLineFromArgv({"has\ttab"}) == "\"has\ttab\"");
    +    KJ_EXPECT(CommandLineFromArgv({R"(C:\Program Files\bitcoin\bitcoin-node.exe)", "-ipcfd", "4"}) ==
    +              R"("C:\Program Files\bitcoin\bitcoin-node.exe" -ipcfd 4)");
    +
    +    // Embedded quotes are backslash-escaped.
    +    KJ_EXPECT(CommandLineFromArgv({R"(say "hi")"}) == R"("say \"hi\"")");
    +    KJ_EXPECT(CommandLineFromArgv({R"(")"}) == R"("\"")");
    +
    +    // Backslashes preceding a quote are doubled; other backslashes are not.
    +    KJ_EXPECT(CommandLineFromArgv({R"(back\\"slash quote)"}) == R"("back\\\\\"slash quote")");
    +
    +    // Backslashes at the end of a quoted argument precede the closing quote,
    +    // so they must be doubled too, or the closing quote would be read as an
    +    // escaped literal quote and the argument would swallow the rest of the
    +    // command line.
    +    KJ_EXPECT(CommandLineFromArgv({R"(trailing backslash\)"}) == R"("trailing backslash\\")");
    +    KJ_EXPECT(CommandLineFromArgv({R"(trailing backslashes\\)"}) == R"("trailing backslashes\\\\")");
    +    KJ_EXPECT(CommandLineFromArgv({R"(mix \" of \\" things\)"}) == R"("mix \\\" of \\\\\" things\\")");
    +}
    +
    +#ifdef WIN32
    +KJ_TEST("CommandLineFromArgv round-trips through CommandLineToArgvW")
    +{
    +    //! Argument vectors covering the CommandLineToArgvW quoting rules: plain
    +    //! arguments, spaces and tabs, embedded quotes, backslashes in various
    +    //! positions, and realistic Windows paths.
    +    const std::vector<std::vector<std::string>> quoting_cases{
    +        {"simple"},
    +        {"a", "b", "c"},
    +        {""},
    +        {"a", "", "b"},
    +        {"has space"},
    +        {"has\ttab"},
    +        {R"(say "hi")"},
    +        {R"(")"},
    +        {R"(\)"},
    +        {R"(C:\a\b)"},
    +        {R"(C:\Program Files\bitcoin\bitcoin-node.exe)", "-ipcfd", "4"},
    +        {R"(\\.\pipe\mp-1234-1)"},
    +        {R"(trailing backslash\)"},
    +        {R"(trailing backslashes\\)"},
    +        {R"(back\\"slash quote)"},
    +        {R"(mix \" of \\" things\)"},
    +    };
    +
    +    for (const auto& argv : quoting_cases) {
    +        // Prepend a plain program name: CommandLineToArgvW parses the first
    +        // token with simpler rules (no backslash escaping), so only the
    +        // remaining arguments exercise the quoting logic under test.
    +        std::vector<std::string> args{"prog"};
    +        args.insert(args.end(), argv.begin(), argv.end());
    +
    +        const std::string cmd{CommandLineFromArgv(args)};
    +        // Test arguments are ASCII, so widening by casting is fine.
    +        const std::wstring wcmd{cmd.begin(), cmd.end()};
    +
    +        int argc{0};
    +        LPWSTR* wargv{CommandLineToArgvW(wcmd.c_str(), &argc)};
    +        KJ_ASSERT(wargv != nullptr, cmd);
    +        KJ_EXPECT(argc == static_cast<int>(args.size()), cmd, argc);
    +        for (int i = 0; i < argc && i < static_cast<int>(args.size()); ++i) {
    +            const std::wstring warg{wargv[i]};
    +            const std::string arg{warg.begin(), warg.end()};
    +            KJ_EXPECT(arg == args[i], cmd, i, arg);
    +        }
    +        LocalFree(wargv);
    +    }
    +}
    +#endif
    +
    +} // namespace test
    +} // namespace mp
    

    ryanofsky commented at 8:40 PM on July 29, 2026:

    re: #231 (review)

    Thanks! Added these tests and confirmed bugfix makes sense and is valid (both new tests fail without it)

  46. Sjors commented at 2:55 PM on July 23, 2026: member

    Roughly reviewed 86aa0296b7ff93e9810e8863dd863a050d17e1b3 util: Add Windows support. Windows code gives me a headache, but it's nicely constrained to a few places.

  47. sedited referenced this in commit a9d181f2d3 on Jul 29, 2026
  48. ryanofsky force-pushed on Jul 29, 2026
  49. ryanofsky commented at 11:26 PM on July 29, 2026: collaborator

    Thanks for the reviews!

    <!-- begin push-13 -->

    Updated 8a682fea6ad99730f718398347c556649a5bbaf1 -> 0d536801bd20aa87aba96b84f0d5bc8d9fab36fb (pr/win.12 -> pr/win.13, compare)<!-- end --> fixing CommandLineFromArgv final quoting bug and adding tests. Also cleaning up some casts and and ifdef.

  50. ryanofsky closed this on Jul 30, 2026

  51. ryanofsky reopened this on Jul 30, 2026

  52. hebasto commented at 8:27 PM on July 30, 2026: member

    Concept and approach ACK.

  53. ryanofsky force-pushed on Jul 30, 2026
  54. ryanofsky commented at 9:06 PM on July 30, 2026: collaborator

    <!-- begin push-14 -->

    Rebased 0d536801bd20aa87aba96b84f0d5bc8d9fab36fb -> b7e367622070a05d3cb14b8e6f06b2f2259ab0b1 (pr/win.13 -> pr/win.14, compare)<!-- end --> due to silent conflict with #311 https://github.com/bitcoin-core/libmultiprocess/actions/runs/30571231743/job/90968191947?pr=231

  55. Kino1994 referenced this in commit 33bd9b5d17 on Aug 2, 2026
  56. in test/mp/test/spawn_tests.cpp:14 in 25443b923e
      10 | @@ -11,6 +11,7 @@
      11 |  #include <condition_variable>
      12 |  #include <csignal>
      13 |  #include <cstdlib>
      14 | +#include <functional>
    


    ViniciusCestarii commented at 1:29 PM on August 3, 2026:

    In "Improve SpawnProcess API and documentation" 25443b923eaaa518ed06855845a68dd9f1ca2b23

    nit: this #include <functional> seems unrelated and isn't needed here.


    ryanofsky commented at 3:34 AM on August 5, 2026:

    re: #231 (review)

    nit: this #include <functional> seems unrelated and isn't needed here.

    Thanks, dropped this include

    EDIT: added it back, since it's needed to call SpawnProcess and there are IWYU errors without it


    ViniciusCestarii commented at 11:58 AM on August 5, 2026:

    Sorry, I tried locally with a wrong command at first. But now I was able to reproduce this IWYU error and confirm that the include is necessary.

  57. in src/mp/util.cpp:268 in 7dcb54db4b
     263 | +    // Start child process
     264 | +    std::string cmd{CommandLineFromArgv(spawn_argv(pipe_path))};
     265 | +    STARTUPINFOA si{};
     266 | +    si.cb = sizeof(si);
     267 | +    PROCESS_INFORMATION pi{};
     268 | +    KJ_WIN32(CreateProcessA(/*lpApplicationName=*/nullptr, const_cast<char*>(cmd.c_str()), /*lpProcessAttributes=*/nullptr, /*lpThreadAttributes=*/nullptr, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/nullptr, /*lpCurrentDirectory=*/nullptr, &si, &pi), "CreateProcess failed");
    


    ViniciusCestarii commented at 2:08 PM on August 3, 2026:

    In "util: Add Windows support" 7dcb54db4bb707009081ab358c9a247a43d577fb

    The Unix branch tries to close all fds that child doesn't need before exec, but the Windows branch leaks everything inheritable with bInheritHandles=TRUE to the child process instead of only the necessary, including the both ends of the socketpair.


    ryanofsky commented at 3:33 AM on August 5, 2026:

    re: #231 (review)

    The Unix branch tries to close all fds that child doesn't need before exec, but the Windows branch leaks everything inheritable with bInheritHandles=TRUE to the child process instead of only the necessary, including the both ends of the socketpair.

    Makes sense. There should be no need for inherited handles so now setting this to false.

  58. in src/mp/util.cpp:260 in 7dcb54db4b
     252 | @@ -236,44 +253,111 @@ std::tuple<ProcessId, SocketId> SpawnProcess(const std::function<std::vector<std
     253 |          _exit(127);
     254 |      }
     255 |      return {pid, fds[1]};
     256 | +#else
     257 | +    // Create windows pipe to send socket over to child process.
     258 | +    static std::atomic<int> counter{1};
     259 | +    std::string pipe_path{"\\\\.\\pipe\\mp-" + std::to_string(GetCurrentProcessId()) + "-" + std::to_string(counter.fetch_add(1))};
     260 | +    HANDLE pipe{CreateNamedPipeA(pipe_path.c_str(), PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_WAIT, /*nMaxInstances=*/1, /*nOutBufferSize=*/0, /*nInBufferSize=*/0, /*nDefaultTimeOut=*/0, /*lpSecurityAttributes=*/nullptr)};
    


    ViniciusCestarii commented at 4:51 PM on August 3, 2026:

    In "util: Add Windows support" 7dcb54db4bb707009081ab358c9a247a43d577fb

    CreateNamedPipeA sets the flag PIPE_WAIT and then calls ConnectNamedPipe so if the child never connects to the pipe (exits or crashes before), the parent blocks in ConnectNamedPipe forever.

    KJ_TEST("SpawnProcess does not hang if child never connects to the pipe")
    {
        // The named pipe SpawnProcess uses to hand the socket to the child is
        // created with PIPE_WAIT and ConnectNamedPipe
        // is called without an OVERLAPPED structure. If the child exits, crashes,
        // or is a program that never opens the pipe, that call blocks forever and
        // the parent hangs with no way to recover.
        //
        // Run SpawnProcess on a detached thread so the test can time out instead of
        // hanging the whole suite. The thread is detached rather than joined because
        // a hung SpawnProcess is never joinable; the state it touches is therefore
        // shared by value, and the leaked thread is reclaimed at process exit.
        // Either returning or throwing counts as a pass: the requirement is only
        // that it does not block indefinitely.
        auto done{std::make_shared<std::atomic<bool>>(false)};
    
        std::thread spawn([done] {
            try {
                const auto [process, socket]{SpawnProcess([](std::string connect_info) -> std::vector<std::string> {
                    // A child that runs to completion without ever opening the pipe
                    // named by connect_info.
                    return {"cmd.exe", "/c", "exit 0"};
                })};
                closesocket(socket);
                CloseHandle(process);
            } catch (...) {
                // SpawnProcess reporting an error is an acceptable outcome; only
                // blocking forever is not.
            }
            done->store(true);
        });
        spawn.detach();
    
        const auto deadline{std::chrono::steady_clock::now() + FAILURE_TIMEOUT};
        while (!done->load() && std::chrono::steady_clock::now() < deadline) {
            std::this_thread::sleep_for(std::chrono::milliseconds{10});
        }
    
        KJ_EXPECT(done->load(), "SpawnProcess blocked waiting for a child that never connected");
    }
    

    ryanofsky commented at 3:33 AM on August 5, 2026:

    re: #231 (review)

    Thanks! Added this test and a change to detect this in error in a new commit. I think this case would most likely arise in practice as a result of a bug, but good to show an error instead of hanging if this happens.

  59. in src/mp/util.cpp:259 in 7dcb54db4b
     252 | @@ -236,44 +253,111 @@ std::tuple<ProcessId, SocketId> SpawnProcess(const std::function<std::vector<std
     253 |          _exit(127);
     254 |      }
     255 |      return {pid, fds[1]};
     256 | +#else
     257 | +    // Create windows pipe to send socket over to child process.
     258 | +    static std::atomic<int> counter{1};
     259 | +    std::string pipe_path{"\\\\.\\pipe\\mp-" + std::to_string(GetCurrentProcessId()) + "-" + std::to_string(counter.fetch_add(1))};
    


    ViniciusCestarii commented at 7:10 PM on August 3, 2026:

    In "util: Add Windows support" 7dcb54db4bb707009081ab358c9a247a43d577fb

    nit: R"()" here too, to match the util_tests.cpp

        std::string pipe_path{R"(\\.\pipe\mp-)" + std::to_string(GetCurrentProcessId()) + "-" + std::to_string(counter.fetch_add(1))};
    

    ryanofsky commented at 3:31 AM on August 5, 2026:

    re: #231 (review)

    Thanks, switched to raw string here

  60. in test/mp/test/listen_tests.cpp:52 in 749e76a530


    ViniciusCestarii commented at 12:57 PM on August 4, 2026:

    In "test: fix listen_tests to compile and run on Windows" 749e76a53047b5933ea9dd4aa2947d5a34f341b8

    nit: TCP here isn't just a Wine workaround, it's what kj win32Socketpair uses on Windows, so the test matches the real transport.


    ryanofsky commented at 3:29 AM on August 5, 2026:

    re: #231 (review)

    nit: TCP here isn't just a Wine workaround, it's what kj win32Socketpair uses on Windows, so the test matches the real transport.

    That's a good observation, and the the windows code does use TCP sockets for the socketpair and spawned proceses. But it uses UNIX sockets to make things like -ipcbind and the bitcoin IPC mining interface work. Since this file is supposed to test listening and connecting it would make the most sense for it to test UNIX sockets on all platforms so the choice to use TCP here really is just a workaround for wine. Updated the comment to be clear that UNIX sockets do work on windows, and this workaround mostly exists for wine


    ViniciusCestarii commented at 12:25 PM on August 5, 2026:

    Thanks for the explanation. I had forgotten the -ipcbind uses AF_UNIX even on Windows, so testing UNIX sockets does make more sense here.

  61. in test/mp/test/listen_tests.cpp:44 in 493a4b074c
      38 | @@ -39,6 +39,19 @@
      39 |  #include <sys/un.h>
      40 |  #endif
      41 |  
      42 | +#ifdef WIN32
      43 | +// Call WSAStartup before any test runs. Winsock requires WSAStartup before any
      44 | +// socket call; the mp library calls it inside ConnectSocketToProcess(), but
    


    ViniciusCestarii commented at 1:13 PM on August 4, 2026:

    In "test: Initialize Winsock in listen_tests.cpp on Windows" 493a4b074c34d4819b14ae2042e96ec44c2842c2

    nit: this comment and the commit message mentions ConnectSocketToProcess but it doens't exist in the codebase. The WSAStartup call is in StartSpawned()

    // socket call; the mp library calls it inside StartSpawned(), but
    

    ryanofsky commented at 3:30 AM on August 5, 2026:

    re: #231 (review)

    nit: this comment and the commit message mentions ConnectSocketToProcess but it doens't exist in the codebase. The WSAStartup call is in StartSpawned()

    Good catch I think that referred to an older version of the code

  62. ViniciusCestarii commented at 1:39 PM on August 4, 2026: contributor

    Approach ACK b7e367622070a05d3cb14b8e6f06b2f2259ab0b1.

    Looking good, build locally with mingw and ran tests and examples with wine.

    I found a couple of issues in SpawnProcess that I think are worth addressing and left some nits.

  63. ryanofsky referenced this in commit d541023c6d on Aug 5, 2026
  64. ryanofsky force-pushed on Aug 5, 2026
  65. ryanofsky commented at 3:41 AM on August 5, 2026: collaborator

    <!-- begin push-15 -->

    Updated b7e367622070a05d3cb14b8e6f06b2f2259ab0b1 -> be00288b992590ff29f96eeb1ee3247147600a41 (pr/win.14 -> pr/win.15, compare)<!-- end --> with suggested changes. Also fixed pthreads include to avoid pthread_self compile error in mingw builds where pthreads is present, as described in #318 (comment)

    <!-- begin push-16 -->

    Updated be00288b992590ff29f96eeb1ee3247147600a41 -> ad7562e9383065e161d88df9fed9aaae0ce06ab2 (pr/win.15 -> pr/win.16, compare)<!-- end --> to fix IWYU errors: add <unistd.h> to listen_tests.cpp, move <atomic> inside #ifdef WIN32 and add <functional> in spawn_tests.cpp https://github.com/bitcoin-core/libmultiprocess/actions/runs/30972705059/job/92200276090

    <!-- begin push-17 -->

    Rebased ad7562e9383065e161d88df9fed9aaae0ce06ab2 -> 19f7d315a5dc547dce2258541decfb6db984b657 (pr/win.16 -> pr/win.17, compare)<!-- end --> to fix silent conflict with #310 https://github.com/bitcoin-core/libmultiprocess/actions/runs/30974304265/job/92205053288?pr=231

  66. ryanofsky referenced this in commit b2653a6a99 on Aug 5, 2026
  67. ryanofsky force-pushed on Aug 5, 2026
  68. ryanofsky referenced this in commit dbdc118d8d on Aug 5, 2026
  69. ryanofsky force-pushed on Aug 5, 2026
  70. in src/mp/util.cpp:280 in 61ad1ed903 outdated
     275 | +    // Send socket to the child via the pipe
     276 | +    KJ_WIN32(ConnectNamedPipe(pipe, nullptr) || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed");
     277 | +    DWORD wr;
     278 | +    KJ_WIN32(WriteFile(pipe, &info, sizeof(info), &wr, nullptr) && wr == sizeof(info), "WriteFile(pipe) failed");
     279 | +    KJ_WIN32(CloseHandle(pipe), "CloseHandle(pipe)");
     280 | +
    


    ViniciusCestarii commented at 12:12 PM on August 6, 2026:

    In "util: Add Windows support" 61ad1ed9030b62abab65585be3a1b9e0efc74b1c

    fds[0], the child's end of the socket pair, should be closed on the parent side, like the unix code does, so each end stays open in only one process.

    CloseSocket(fds[0]);
    

    Without this, using the examples, killing mpprinter never calls Connection.onDisconnect, and the next call hangs: the request is sent and no response ever arrives.

    The existing disconnect tests in test/mp/test/test.cpp use newTwoWayPipe() and they never exercise SpawnProcess, which is why this isn't caught today. I think it might be worth a test for this as a follow up.


    ryanofsky commented at 8:13 PM on August 6, 2026:

    re: #231 (review)

    fds[0], the child's end of the socket pair, should be closed on the parent side, like the unix code does, so each end stays open in only one process.

    Thanks this is a good find and from what I can tell it looks like the missing CloseSocket will probably lead to a small resource leak in the parent process after the child process exits.

    But I'm less sure about the potential hang. This code has been around for a long time I think I would have seen the hang it was happening during normal execution. Unless you're saying you have to manually kill a child process to see the hang?

    It is true mptest doesn't have automated test coverage for this code, and it might be nice to add either by by allowing the unit test executable to be spawned or adding new executables, or by testing the example executables. But this does test coverage in Bitcoin core that is run in https://github.com/bitcoin/bitcoin/pull/32387 in the rpc_misc.py echoipc test.

    To address the leak and potential hang I added the suggeted CloseSocket call and I also moved the WSADuplicateSocket call as close as possible to the WriteFile call to reduce the chances of a leak from WSADuplicateSocket.


    ViniciusCestarii commented at 1:14 PM on August 7, 2026:

    Unless you're saying you have to manually kill a child process to see the hang?

    I tested killing the child because it is the easiest way to reproduce it, but this hang could happen from the child crashing, exiting, aborting, anything that makes the child close its socket end and, since the parent still had the child socket end open in its process, on the next read for a reply it wouldn't receive EOF but hang in there for a reply. The leaked handle is exactly what suppressed the disconnect: EOF only arrives once every handle to the child's end is closed, and the parent was still holding one.

    Tested the new changes 0fcfa78d90759aec5a71646d38bbea124942a9e9 and can confirm that now killing the child disconnects instead of hanging.

  71. in src/mp/util.cpp:281 in 61ad1ed903 outdated
     276 | +    KJ_WIN32(ConnectNamedPipe(pipe, nullptr) || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed");
     277 | +    DWORD wr;
     278 | +    KJ_WIN32(WriteFile(pipe, &info, sizeof(info), &wr, nullptr) && wr == sizeof(info), "WriteFile(pipe) failed");
     279 | +    KJ_WIN32(CloseHandle(pipe), "CloseHandle(pipe)");
     280 | +
     281 | +    return {pi.hProcess, fds[1]};
    


    ViniciusCestarii commented at 12:41 PM on August 6, 2026:

    In "util: Add Windows support" 61ad1ed9030b62abab65585be3a1b9e0efc74b1c

    Handles and sockets can leak if any call below CreateNamedPipeA throws, and a failure after CreateProcessA leaves a running child with no handle to wait on or terminate (essentially an orphaned child process). I believe fixing this could be a follow up since it would mean a larger diff here.


    ryanofsky commented at 7:45 PM on August 6, 2026:

    re: #231 (review)

    Yeah added a TODO if someone wants to address this. In general I'm more concerned about reporting errors in this code than trying to handle them. If this code fails it means there is either a bug in which case having logs that indicate where the bug is is the most important thing, or there are unexpected external events like processes being killed or resources being exhausted which may mean there is no good error handling strategy and error reporting is still the most important thing.

  72. ViniciusCestarii commented at 1:35 PM on August 6, 2026: contributor

    Reviewed and tested new changes, built with mingw and ran mpexample and tests on Windows 11. The new changes look good.

    But I noticed that fds[0] is kept open in the parent, so, if the child dies, the parent won't notice and the next call will hang.

    edit: the current pipeline failed because IWYU is flagging #include <unistd.h> on liten_tests.cpp

  73. ryanofsky referenced this in commit ef7a7a2f16 on Aug 6, 2026
  74. ryanofsky force-pushed on Aug 6, 2026
  75. ryanofsky commented at 8:18 PM on August 6, 2026: collaborator

    Thanks for the review!

    <!-- begin push-18 -->

    Updated 19f7d315a5dc547dce2258541decfb6db984b657 -> 0fcfa78d90759aec5a71646d38bbea124942a9e9 (pr/win.17 -> pr/win.18, compare)<!-- end --> adding CloseSocket call as suggested to fix memory leak, updating comments, and fixing iwyu errors https://github.com/bitcoin-core/libmultiprocess/actions/runs/30976202207/job/92210589405

  76. DrahtBot added the label Needs rebase on Aug 11, 2026
  77. ryanofsky referenced this in commit c206baa45c on Aug 12, 2026
  78. ryanofsky referenced this in commit 52505643c5 on Aug 13, 2026
  79. ryanofsky referenced this in commit 4197662520 on Aug 13, 2026
  80. util: Improve SpawnProcess API and documentation
    Remove recently introduced SpawnConnectInfo and SpawnConnectInfoToArgsFn type
    aliases since they are the same on all platforms and might obscure the fact
    that connect info should be treated as an opaque string.
    
    Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
    be5f87655a
  81. util, test: Add CloseSocket, use SocketId
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    15aa914f03
  82. util, test: Replace UnixListener with SocketListener
    Replace the Unix-only UnixListener test helper with a cross-platform
    SocketListener backed by a std::variant<sockaddr_un> (TCP arm will be
    added later for Wine compatibility).
    
    - test/socketlistener.h: New header with SocketListener class extracted from
      the pattern of UnixListener but using SocketId/SocketError types,
      std::filesystem for temp dir cleanup, and mp::CloseSocket for teardown.
      Uses std::variant<sockaddr_un> so the TCP arm can be appended later without
      restructuring the class.
    - test/unixlistener.h: Deleted.
    - test/listen_tests.cpp: Switch to socketlistener.h and socketlistener.
    - test/connect_tests.cpp: Switch to socketlistener.h and socketlistener.
    
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    c79b49afe1
  83. util, test: guard Unix-only code for Windows build
    Move POSIX-only headers in util.cpp into a #ifndef WIN32 block so the file
    compiles on Windows without modification. Guard the Unix-only helpers (MakeArgv,
    MaxFd, ChildFail, SpawnError*, ReadSpawnResult, WriteSpawnError, KillAndReapChild)
    with a single #ifndef WIN32 block. Guard the extern "C" environ declaration
    likewise. Guard Unix-only includes in spawn_tests.cpp and connect_tests.cpp and
    wrap the Unix-only spawn tests with #ifndef WIN32.
    
    No Windows implementations are added here; those come in a later commit.
    
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    860e2ffb5c
  84. util: Add Windows CommandLineFromArgv escaping function
    Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
    ee09c2b269
  85. 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.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.
    
    Co-Authored-By: ViniciusCestarii <viniciuscestari01@gmail.com>
    7861351f24
  86. util: Fix Windows SpawnProcess hang when child exits before connecting to named pipe
    SpawnProcess on Windows creates a named pipe with PIPE_WAIT and then calls
    ConnectNamedPipe synchronously. If the child exits or crashes before
    connecting, ConnectNamedPipe blocks forever with no recovery path.
    
    Fix by opening the pipe with FILE_FLAG_OVERLAPPED and using
    WaitForMultipleObjects on both the connect event and the child process
    handle. If the process handle signals first, the child died without
    connecting and SpawnProcess throws instead of hanging. Since the pipe is
    now in overlapped mode, WriteFile also requires an OVERLAPPED structure;
    use GetOverlappedResult with bWait=TRUE to handle both synchronous and
    asynchronous completion.
    
    Add a Windows-only test that spawns a child which exits immediately
    without opening the named pipe and asserts SpawnProcess does not block.
    
    (https://github.com/bitcoin-core/libmultiprocess/pull/231#discussion_r3706021950)
    
    Co-Authored-By: ViniciusCestarii <viniciuscestari01@gmail.com>
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    97011d4008
  87. util: make pthreads optional on Windows to enable MSVC builds
    Allow code to compile without pthreads available, as required for MSVC
    compatibility. Avoid unconditional POSIX calls (fork, posix_spawn,
    pthread_getname_np) by moving them into #ifndef WIN32 or HAVE_PTHREAD_*
    guards. When pthreads is available on Windows (detected via cmake
    HAVE_PTHREAD_* checks), still use it for thread name reporting since it
    provides useful information at low cost.
    
    Also add Threads::Threads as an explicit dependency of the multiprocess
    library. proxy.cpp directly uses thread_local, std::this_thread, and
    std::thread, and the dependency was previously satisfied only through
    transitive linkage from CapnProto::kj-async.
    
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    1938c199d7
  88. test: Add TCP SocketListener and Windows compat to socketlistener.h
    Add TCP (sockaddr_in) support to SocketListener and platform-conditional
    header guards so the class compiles and works on Windows:
    
    - Wrap POSIX headers (arpa/inet.h, netinet/in.h, sys/socket.h, sys/un.h)
      in #else of #ifdef WIN32, with Windows equivalents (afunix.h,
      ws2tcpip.h) in the #ifdef WIN32 branch.
    - Add a WsaInit static initializer on Windows that calls WSAStartup
      before any socket operations. Tests create sockets directly (not via
      StartSpawned), so Winsock must be initialized here.
    - Add Init(sockaddr_in&) and Connect(const sockaddr_in&) overloads for
      TCP loopback connections.
    - Use TCP on Windows to work around Wine's lack of the AcceptEx extension
      required by KJ's AF_UNIX listener; Unix platforms continue using
      AF_UNIX.
    - Expand std::variant<sockaddr_un> to std::variant<sockaddr_in, sockaddr_un>.
    
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    ae6cf017d4
  89. ci: add Windows cross-compilation config using MinGW and Wine
    - shell.nix: add `windows` parameter that selects pkgs.pkgsCross.mingwW64
      as the cross target; also change crossPkgs default from import<nixpkgs>{}
      to null (cleaner API). When windows=true, add native pkgs.capnproto to
      nativeBuildInputs so capnp/capnpc-c++ are in PATH for cmake code generation,
      and add wine64Packages.staging so ctest can run mptest.exe via wine.
      Change llvmBase to always use pkgs (native) instead of crossPkgs.
    
    - ci/configs/windows.bash: new config that cross-compiles with mingw,
      sets CMAKE_SYSTEM_NAME=Windows, CMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER
      (so cmake finds native capnp from PATH), CMAKE_CROSSCOMPILING_EMULATOR=wine
      (so ctest runs mptest.exe via wine), and sets MPGEN_PRE_BUILD=1.
    
    - ci/scripts/ci.sh: add MPGEN_PRE_BUILD support: when set, build native mpgen
      in $CI_DIR-native before the main cross build, then inject
      -DMPGEN_EXECUTABLE into CMAKE_ARGS. This is needed because cmake's
      add_custom_command does not use CMAKE_CROSSCOMPILING_EMULATOR, so the
      cross-compiled mpgen.exe cannot be used as a code generator directly.
    
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    d0eea62c58
  90. ryan8or referenced this in commit 7ad6607219 on Aug 13, 2026
  91. ryan8or force-pushed on Aug 13, 2026
  92. ryanofsky commented at 8:48 PM on August 13, 2026: collaborator

    <!-- begin push-19 -->

    Rebased 0fcfa78d90759aec5a71646d38bbea124942a9e9 -> 3959d69f478ab056826ecbf05cd9cf0326bf31de (pr/win.18 -> pr/win.19, compare)<!-- end --> due to conflicts with #298, #312, and #324

    <!-- begin push-20 -->

    Updated 3959d69f478ab056826ecbf05cd9cf0326bf31de -> d0eea62c58928f9660b8dc5541a62ea395c565ef (pr/win.19 -> pr/win.20, compare)<!-- end --> with minor test and commit message cleanups

  93. ryanofsky force-pushed on Aug 13, 2026
  94. DrahtBot removed the label Needs rebase on Aug 13, 2026
  95. ryanofsky referenced this in commit df65470848 on Aug 15, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/libmultiprocess. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-26 01:30 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me