Skip to content

Add ReadList helper#285

Merged
ryanofsky merged 2 commits into
bitcoin-core:masterfrom
ViniciusCestarii:add-readlist-helper
Jul 5, 2026
Merged

Add ReadList helper#285
ryanofsky merged 2 commits into
bitcoin-core:masterfrom
ViniciusCestarii:add-readlist-helper

Conversation

@ViniciusCestarii

@ViniciusCestarii ViniciusCestarii commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Follow up of #277 (review). This adds ReadList helper and uses it to dedup the CustomReadField implementations for std::map, std::set, std::unordered_set, and std::vector, mirroring the existing BuildList helper on the build side.

I took the liberty of adding the commit f2a734a "type: reserve first when reading std::unordered_set" so it reserves the correct capacity first and then emplaces the values.

@DrahtBot

DrahtBot commented Jun 3, 2026

Copy link
Copy Markdown

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

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK ryanofsky
Stale ACK xyzconstant

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #288 (Create support branch for CI scripts, documentation, and examples by ryanofsky)

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.

@ryanofsky ryanofsky left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Concept ACK f2a734a. Thanks for the followup. Plan to review more after #277 is merged

Comment thread include/mp/type-map.h Outdated
@ViniciusCestarii

Copy link
Copy Markdown
Contributor Author

Forced push 245f792 to just rebase with master

@ViniciusCestarii

Copy link
Copy Markdown
Contributor Author

Forced push 5d23f83 applying @ryanofsky suggestion. It reads cleaner and dedup more code.

@ViniciusCestarii ViniciusCestarii marked this pull request as ready for review June 11, 2026 12:55
Comment thread include/mp/type-vector.h Outdated
@ViniciusCestarii

Copy link
Copy Markdown
Contributor Author

Forced push 043b8a8 applying @xyzconstant suggestion

@xyzconstant

Copy link
Copy Markdown
Contributor

tACK 043b8a8

Thanks for the updates @ViniciusCestarii.

This is a simple refactor that moves common code to ReadList, consistent with existing BuildList helper.

The changes are clean and can be merged as-is.

@DrahtBot DrahtBot requested a review from ryanofsky June 23, 2026 14:32
Comment thread include/mp/proxy-types.h Outdated
}

template <typename LocalType, typename Input, typename ReadDest, typename InitFn, typename EmplaceFn>
void ReadList(TypeList<LocalType>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest, InitFn&& init, EmplaceFn&& emplace)

@ryanofsky ryanofsky Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

In commit "proxy: add ReadList helper and dedup map/set/vector read handlers" (1bfc594)

There seems to be a regression here. It would be better for this to return decltype(auto) and use return read_dest.update(...) below so these CustomReadField functions are compatible with ReadDestTemp. ReadDestTemp is not documented or covered very well from the test framework right now but the idea behind it is to support returning types like UniValue::type_error that can't be modified after they are constructed.

Fortunately the current unit tests do include one cases where ReadDestTemp is used (even though it isn't really needed and ReadDestUpdate would be a better fit), so it can be extended to test ReadList return value. A full fix and test is below that will result in a compile error if ReadList returns void. Feel free to only add the return value fix here, since the test could be a followup and I started working on a separate change try to test ReadDestTemp more fully. Also feel free to add the test to this PR. I would just suggest if adding it, to add it as a new initial commit before the other changes so the refactoring is separate.

diff

diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h
index 66310d9..f1127da 100644
--- a/include/mp/proxy-types.h
+++ b/include/mp/proxy-types.h
@@ -292,9 +292,9 @@ void BuildList(TypeList<LocalType>, InvokeContext& invoke_context, Output&& outp
 }
 
 template <typename LocalType, typename Input, typename ReadDest, typename InitFn, typename EmplaceFn>
-void ReadList(TypeList<LocalType>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest, InitFn&& init, EmplaceFn&& emplace)
+decltype(auto) ReadList(TypeList<LocalType>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest, InitFn&& init, EmplaceFn&& emplace)
 {
-    read_dest.update([&](auto& value) {
+    return read_dest.update([&](auto& value) {
         auto data = input.get();
         init(value, data.size());
         for (auto item : data) {
diff --git a/test/mp/test/foo-types.h b/test/mp/test/foo-types.h
index b96eabf..1bc6c52 100644
--- a/test/mp/test/foo-types.h
+++ b/test/mp/test/foo-types.h
@@ -46,6 +46,7 @@ void CustomBuildField(TypeList<FooCustom>, Priority<1>, InvokeContext& invoke_co
 {
     BuildField(TypeList<std::string>(), invoke_context, output, value.v1);
     output.setV2(value.v2);
+    BuildField(TypeList<std::vector<int>>(), invoke_context, output, value.v3);
 }
 
 template <typename Input, typename ReadDest>
@@ -55,6 +56,7 @@ decltype(auto) CustomReadField(TypeList<FooCustom>, Priority<1>, InvokeContext&
     return read_dest.update([&](FooCustom& value) {
         value.v1 = ReadField(TypeList<std::string>(), invoke_context, mp::Make<mp::ValueField>(custom.getV1()), ReadDestTemp<std::string>());
         value.v2 = custom.getV2();
+        value.v3 = ReadField(TypeList<std::vector<int>>(), invoke_context, mp::Make<mp::ValueField>(custom.getV3()), ReadDestTemp<std::vector<int>>());
     });
 }
 
diff --git a/test/mp/test/foo.capnp b/test/mp/test/foo.capnp
index 3173645..89d6f90 100644
--- a/test/mp/test/foo.capnp
+++ b/test/mp/test/foo.capnp
@@ -64,6 +64,7 @@ struct FooStruct $Proxy.wrap("mp::test::FooStruct") {
 struct FooCustom $Proxy.wrap("mp::test::FooCustom") {
     v1 @0 :Text;
     v2 @1 :Int32;
+    v3 @2 :List(Int32);
 }
 
 struct FooEmpty $Proxy.wrap("mp::test::FooEmpty") {
diff --git a/test/mp/test/foo.h b/test/mp/test/foo.h
index 5b66b85..54fd043 100644
--- a/test/mp/test/foo.h
+++ b/test/mp/test/foo.h
@@ -33,6 +33,7 @@ struct FooCustom
 {
     std::string v1;
     int v2;
+    std::vector<int> v3;
 };
 
 struct FooEmpty
diff --git a/test/mp/test/test.cpp b/test/mp/test/test.cpp
index eb4b5ec..86fd1bf 100644
--- a/test/mp/test/test.cpp
+++ b/test/mp/test/test.cpp
@@ -214,9 +214,11 @@ KJ_TEST("Call FooInterface methods")
     FooCustom custom_in;
     custom_in.v1 = "v1";
     custom_in.v2 = 5;
+    custom_in.v3 = {10, 20, 30};
     FooCustom custom_out = foo->passCustom(custom_in);
     KJ_EXPECT(custom_in.v1 == custom_out.v1);
     KJ_EXPECT(custom_in.v2 == custom_out.v2);
+    KJ_EXPECT(custom_in.v3 == custom_out.v3);
 
     foo->passEmpty(FooEmpty{});
 

@ViniciusCestarii ViniciusCestarii Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nice thanks for the explanation, I only aplied the return decltype(auto) bit on 4d0f8db, so all the tests could be in its own PR

@DrahtBot DrahtBot requested a review from ryanofsky July 1, 2026 22:55
Comment thread include/mp/type-vector.h Outdated
@DrahtBot DrahtBot requested a review from ryanofsky July 2, 2026 00:58
Comment thread include/mp/type-set.h
value.clear();
for (auto item : data) {
ReadField(TypeList<LocalType>(), invoke_context, Make<ValueField>(item),
ReadDestEmplace(TypeList<const LocalType>(), [&](auto&&... args) -> auto& {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

In commit "type: reserve first when reading std::unordered_set" (043b8a8)

Note: new code is dropping const LocalType here which I could look like a problem, because std::set members are const and can't be updated. So this might seem to break cases like std::set<std::shared_ptr<T>> because shared_ptr readfield uses read_dest.update and would be unable to update the new emplaced set element. But this works because the is_const_v check ignores LocalType entirely and just checks type of the return value of the emplace function. And that is still const below (*value.emplace(std::forward<decltype(args)>(args)...).first is const).

@DrahtBot DrahtBot requested a review from ryanofsky July 2, 2026 01:35
@ViniciusCestarii

Copy link
Copy Markdown
Contributor Author

Thanks for reviewing. Forced push 6450345 applying @ryanofsky suggestions

@ryanofsky ryanofsky left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code review ACK 6450345. Looks great! It's really nice to deduplicate this code and drop vector<bool> specialization.

I have a bunch of commits building on top of this to test the ReadDestTemp use-case that was broken by the earlier version of this PR, also to provide much better documentation for the ReadField/ReadDest code in general, and also to add better support and test coverage for non-default constructible and non-movable types, so full ReadField capabilities are really exercised within libmultiprocess, and not just used downstream in bitcoin/bitcoin#10102 and easily broken by changes like this.

@DrahtBot DrahtBot requested a review from xyzconstant July 5, 2026 18:53
@ryanofsky ryanofsky merged commit 092be51 into bitcoin-core:master Jul 5, 2026
13 checks passed
fanquake pushed a commit to bitcoin/bitcoin that referenced this pull request Jul 7, 2026
…ea02

16bf05dea02 Merge bitcoin-core/libmultiprocess#302: refactor: rename EventLoop::m_num_clients to m_num_refs
dd537da9e40 Merge bitcoin-core/libmultiprocess#301: test: recursive async IPC calls and cleanups
400291de000 Merge bitcoin-core/libmultiprocess#299: ci: remove libevent from Core CIs
092be515adf Merge bitcoin-core/libmultiprocess#285: Add ReadList helper
5b617880c51 Merge bitcoin-core/libmultiprocess#283: Add `makePool` method on `ThreadMap`
d4998304154 refactor: rename EventLoop::m_num_clients to m_num_refs
6450345c985 type: reserve first when reading std::unordered_set
4d0f8db5f99 proxy: add ReadList helper and dedup map/set/vector read handlers
0e49d911867 Add `makePool` method on `ThreadMap`
5519f7f9485 test: recursive async IPC calls
a29ceff40bc ci: remove libevent from Core CIs
8412fcdc659 Merge bitcoin-core/libmultiprocess#295: Mark Waiter m_cv as guarded by m_mutex
1593ee2d18a Merge bitcoin-core/libmultiprocess#294: test: Add passDouble smoke test
9885d7dd33c Merge bitcoin-core/libmultiprocess#286: proxy-client: fix TSan data race in clientDestroy
fa35501c4f0 Mark Waiter m_cv as guarded by m_mutex
faaedb11f8a test: Add passDouble smoke test
733c64318d1 Merge bitcoin-core/libmultiprocess#292: type-number: fix clang-tidy modernize-use-nullptr
9cc3479ab33 Merge bitcoin-core/libmultiprocess#291: cmake: Add `mp_headers` custom target
201abd9e3a5 Merge bitcoin-core/libmultiprocess#289: cmake: make target_capnp_sources use CURRENT dirs
99820c8aecb Merge bitcoin-core/libmultiprocess#279: doc: Add comments to FIELD_* constants in proxy.h
73b985540c5 Merge bitcoin-core/libmultiprocess#278: doc: Fix and expand design.md
e7e91b2e23e Merge bitcoin-core/libmultiprocess#277: Add std::unordered_set support and a helper BuildList to dedup list build handlers
91a951f59ac tidy fix: modernize-use-nullptr
16362f42d01 cmake: Add `mp_headers` custom target
615a94fe3a2 cmake: document ONLY_CAPNP option in target_capnp_sources
90982f75c6b mpgen: iwyu changes required by previous commit
25bb3e67f39 proxy-client: fix TSan data race in clientDestroy
620f297f311 cmake: make target_capnp_sources use CURRENT dirs
9de4b885aa6 test: use camelCase + $Proxy.name for FooStruct fields
011b91793dd type: add std::unordered_set support
20d19b9644e proxy: add BuildList helper and dedup map/set/vector build handlers
e863c6cdf61 doc: Add comments to FIELD_* constants in proxy.h
18db0ab9570 doc: Fix and expand design.md
61de6975362 Merge bitcoin-core/libmultiprocess#273: proxy-client: tolerate exceptions from remote destroy during cleanup
9cec9d6ca55 Merge bitcoin-core/libmultiprocess#243: mpgen: support primitive std::optional struct fields
4aaff113745 Merge bitcoin-core/libmultiprocess#238: cmake, ci: updates for recent nixpkgs
2ac55a56b58 Merge bitcoin-core/libmultiprocess#218: Better error and log messages
6de92e1c732 proxy-client: tolerate exceptions from remote destroy during cleanup
90be8354d47 test: regression for ~ProxyClient destroy after peer disconnect
3c69d125a17 Merge bitcoin-core/libmultiprocess#260: event loop: tolerate unexpected exceptions in `post()` callbacks
b8a48c65e60 event loop: tolerate unexpected exceptions in `post()` callbacks
f787863d2cd Merge bitcoin-core/libmultiprocess#270: doc: Bump version 10 > 11
a22f6029103 doc: Bump version 10 > 11
4eae445d6d8 debug: Add TypeName() function and log statements for Proxy objects being created and destroyed
f326c5b1b7b logging: Add better logging on IPC server-side failures
6dbfa56a040 mpgen: support primitive std::optional struct fields
8d1277deb55 mpgen refactor: add AccessorType function
db716bbcba7 mpgen refactor: Move field handling code to FieldList class
db7acb3ce27 ci: Fix shell.nix compatibility with CMake 4.0
91a7759a9ab cmake: Fix IWYU in nix by adding CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES

git-subtree-dir: src/ipc/libmultiprocess
git-subtree-split: 16bf05dea02651f75733ff08531181aa774fc5a8
fanquake added a commit to bitcoin/bitcoin that referenced this pull request Jul 7, 2026
…ol` method

6b0a907 Squashed 'src/ipc/libmultiprocess/' changes from 3edbe8f67c1..16bf05dea02 (Ryan Ofsky)

Pull request description:

  The changes can be verified by running `test/lint/git-subtree-check.sh src/ipc/libmultiprocess` as described in [developer notes](https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#subtrees) and [lint instructions](https://github.com/bitcoin/bitcoin/tree/master/test/lint#git-subtree-checksh).

  Changes since last subtree update (#34977):

  - Adds `makePool` method on `ThreadMap` to support thread pool routing, allowing requests without a specific client thread to be dispatched to a pool using a shortest-queue strategy ([#283](bitcoin-core/libmultiprocess#283)).
  - Adds `std::unordered_set` support, a `BuildList` helper, and a `ReadList` helper to reduce duplication in list build and read handlers ([#277](bitcoin-core/libmultiprocess#277), [#285](bitcoin-core/libmultiprocess#285)).
  - Adds support for translating C++ `std::optional<T>` struct fields to pairs of `T` + `hasT :Bool` Cap'n Proto struct fields, allowing unset optional primitive fields to be represented ([#243](bitcoin-core/libmultiprocess#243)).
  - Produces more readable log output for Proxy object lifecycle events and IPC server-side failures ([#218](bitcoin-core/libmultiprocess#218)).
  - Handles exceptions thrown by `destroy` methods by logging instead of aborting ([#273](bitcoin-core/libmultiprocess#273)). This can prevent server crashes when non-libmultiprocess clients disconnect without destroying objects, in the case where a server object owns client objects and the server destructor tries to call the disconnected client to free them ([#219](bitcoin-core/libmultiprocess#219)).
  - Handles unexpected exceptions thrown by callbacks (that should never happen) by logging errors instead of deadlocking ([#260](bitcoin-core/libmultiprocess#260)).
  - Fixes a rare mptest hang on musl builds caused by a lost wakeup bug in `Waiter` ([#295](bitcoin-core/libmultiprocess#295)).
  - Fixes a race condition in a log print detected by TSan ([#286](bitcoin-core/libmultiprocess#286)).
  - Build improvements: makes `target_capnp_sources` work correctly when libmultiprocess is used as a CMake subproject ([#289](bitcoin-core/libmultiprocess#289)), adds `mp_headers` target for better lint tool support ([#291](bitcoin-core/libmultiprocess#291)), and fixes compatibility with recent Nix and CMake 4.0 ([#238](bitcoin-core/libmultiprocess#238)).
  - Test, CI, documentation, and minor code improvements: design document corrections ([#278](bitcoin-core/libmultiprocess#278)), field constant comments ([#279](bitcoin-core/libmultiprocess#279)), clang-tidy fix ([#292](bitcoin-core/libmultiprocess#292)), new smoke test for double-precision float values ([#294](bitcoin-core/libmultiprocess#294)), new test for recursive async IPC calls ([#301](bitcoin-core/libmultiprocess#301)), removal of libevent from Core CI builds ([#299](bitcoin-core/libmultiprocess#299)), and rename of `EventLoop::m_num_clients` to `m_num_refs` ([#302](bitcoin-core/libmultiprocess#302)).

ACKs for top commit:
  fanquake:
    ACK 02afa66
  hebasto:
    ACK 02afa66.

Tree-SHA512: ef81a951c971f328a0a98436030467eeea30925eb6016eafd9bc7a25726c87628a852bbb1d84b88bce340aeea2bed25c65bc55db1168ebcb850628cd18808883
ryanofsky added a commit that referenced this pull request Jul 8, 2026
463d073 test: rename vBool to vector_bool (ViniciusCestarii)
85df233 test: add mapStringInt to foo.capnp to cover map serialization and deserialization (ViniciusCestarii)

Pull request description:

  Add test to ensure map serialization is working and will keep working. Useful for #285.

  Also renames `v_bool` to `vector_bool`.

  Best reviewed commit by commit.

ACKs for top commit:
  ryanofsky:
    Code review ACK 463d073

Tree-SHA512: 7f7cd30e9a28ab0813d6068c9d4651a905fa02558cb79ecc500e9fdd684c415447c95ffec8ee17258b2e49415cdadf817b8201d50dae0de798ceb881486483b9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants