Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes.
|
| Layer / File(s) | Summary |
|---|---|
Reactor startup and lifecycle src/extensions/rppgrpc/rppgrpc/client_reactor.hpp |
Bidirectional and read reactors post the first read before StartCall(). The documentation describes possible reactor deletion after init() and warns about races from external emissions without a gRPC hold. |
Usage examples and cancellation coverage src/examples/rppgrpc/doxygen/client_reactor.cpp, src/tests/rppgrpc/test_async_client.cpp |
The bidirectional and write examples post initial requests before init(). The regression test runs 300 cancelled calls per reactor type and checks for a terminal observable notification within five seconds. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix · Severity of issue fixed: Medium
Suggested reviewers: alexinlog
Merge Risk: 
Minimal · up to 6a76a
-
The startup change and updated examples have no identified merge-blocking issue. Normal validation can proceed.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
Full details: Title check
-
Explanation
The title names the primary change: client reactors in rppgrpc now post the first read before StartCall, which matches the changes to client_bidi_reactor::init() and client_read_reactor::init() and the linked issue
#762about a heap-use-after-free in client_read_reactor::init(). It is short, specific, and free of noise. The title does not mention the test addition or the documentation updates, which is acceptable because it need not cover every detail.
Full details: Linked Issues check
-
Explanation
Direct issue
#762requires preventing reactor use afterStartCall()in the read and bidi reactor initialization paths.client_read_reactor::init()andclient_bidi_reactor::init()now post the first read beforeStartCall(), andStartCall()is the final reactor use in each method. The added test covers cancelled calls duringinit()for the read, bidi, and write reactor types and waits for terminal notification. The documentation and Doxygen example match the required pre-init()ordering. Issue#762only identifies later external writes as a suspected related case, so the lack of a new hold for those writes is not an unmet requirement.
Full details: Out of Scope Changes check
-
Explanation
The changed files stay within issue
#762. The header and Doxygen changes document the safe initialization order and the gRPC hold requirement. The test adds regression coverage for call completion duringinit(). The write-reactor test case provides related coverage without changing write initialization behavior. No unrelated production behavior or unrelated feature is shown.
-
-
✨ Finishing Touches
-
🧪 Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
A rabbit posts a read before the call,
Then waits for StartCall() to begin it all.
Three hundred calls through cancel’s chilly night,
Each sends a terminal sign in sight.
The burrow rests; the reactors finish right.
Comment @coderabbitai help to get the list of available commands.
BENCHMARK RESULTS (AUTOGENERATED)
|
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| Subscribe empty callbacks to empty observable | 320.60 ns | 1.56 ns | 1.87 ns | 0.83 | 1.10 ns |
| Subscribe empty callbacks to empty observable via pipe operator | 309.75 ns | 1.56 ns | 1.87 ns | 0.83 | 1.09 ns |
Sources
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| from array of 1 - create + subscribe + immediate | 699.33 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| from array of 1 - create + subscribe + current_thread | 1051.94 ns | 3.45 ns | 3.45 ns | 1.00 | 2.74 ns |
| concat_as_source of just(1 immediate) create + subscribe | 2259.16 ns | 114.54 ns | 128.23 ns | 0.89 | 94.99 ns |
| defer from array of 1 - defer + create + subscribe + immediate | 740.17 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| interval - interval + take(3) + subscribe + immediate | 2144.28 ns | 59.70 ns | 59.66 ns | 1.00 | 50.11 ns |
| interval - interval + take(3) + subscribe + current_thread | 3050.96 ns | 32.76 ns | 32.72 ns | 1.00 | 29.20 ns |
| from array of 1 - create + as_blocking + subscribe + new_thread | 35885.71 ns | 33311.22 ns | 34766.46 ns | 0.96 | 27306.19 ns |
| from array of 1000 - create + as_blocking + subscribe + new_thread | 44805.38 ns | 55782.26 ns | 56094.37 ns | 0.99 | 44053.19 ns |
| concat_as_source of just(1 immediate) and just(1,2 immediate)create + subscribe | 3564.73 ns | 142.20 ns | 148.33 ns | 0.96 | 132.91 ns |
Filtering Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take(1)+subscribe | 1084.56 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just+filter(true)+subscribe | 850.51 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just(1,2)+skip(1)+subscribe | 1015.42 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just(1,1,2)+distinct_until_changed()+subscribe | 891.78 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just(1,2)+first()+subscribe | 1265.15 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just(1,2)+last()+subscribe | 948.26 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just+take_last(1)+subscribe | 1121.27 ns | 18.05 ns | 18.05 ns | 1.00 | 15.28 ns |
| immediate_just(1,2,3)+element_at(1)+subscribe | 838.81 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
Schedulers
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate scheduler create worker + schedule | 264.14 ns | 0.62 ns | 1.56 ns | 0.40 | 0.54 ns |
| current_thread scheduler create worker + schedule | 377.88 ns | 4.98 ns | 4.98 ns | 1.00 | 3.82 ns |
| current_thread scheduler create worker + schedule + recursive schedule | 820.94 ns | 61.31 ns | 62.68 ns | 0.98 | 52.29 ns |
Transforming Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+map(v*2)+subscribe | 846.11 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just+scan(10, std::plus)+subscribe | 917.09 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
| immediate_just+flat_map(immediate_just(v*2))+subscribe | 2347.62 ns | 120.19 ns | 139.91 ns | 0.86 | 154.36 ns |
| immediate_just+buffer(2)+subscribe | 1563.92 ns | 13.70 ns | 13.72 ns | 1.00 | 15.84 ns |
| immediate_just+window(2)+subscribe + subscsribe inner | 2465.18 ns | 1309.86 ns | 1360.01 ns | 0.96 | 1051.23 ns |
Conditional Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take_while(false)+subscribe | 845.07 ns | - | - | 0.00 | - |
| immediate_just+take_while(true)+subscribe | 843.75 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
Utility Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(1)+subscribe_on(immediate)+subscribe | 2006.76 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
Combining Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(immediate_just(1), immediate_just(1)) + merge() + subscribe | 3425.04 ns | 164.50 ns | 157.06 ns | 1.05 | 169.45 ns |
| immediate_just(1) + merge_with(immediate_just(2)) + subscribe | 3692.58 ns | 156.38 ns | 162.67 ns | 0.96 | 158.04 ns |
| immediate_just(1) + with_latest_from(immediate_just(2)) + subscribe | - | 149.40 ns | 145.78 ns | 1.02 | 131.53 ns |
| immediate_just(immediate_just(1),immediate_just(1)) + switch_on_next() + subscribe | 3546.45 ns | 480.85 ns | 418.19 ns | 1.15 | 342.56 ns |
| immediate_just(1) + zip(immediate_just(2)) + subscribe | 2202.34 ns | 229.39 ns | 225.02 ns | 1.02 | 203.01 ns |
| immediate_just(immediate_just(1), immediate_just(1)) + concat() + subscribe | 3232.94 ns | 228.91 ns | 239.71 ns | 0.95 | 229.88 ns |
Subjects
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| publish_subject with 1 observer - on_next | 34.85 ns | 14.81 ns | 14.79 ns | 1.00 | 12.94 ns |
| subscribe 100 observers to publish_subject | 201779.60 ns | 17498.20 ns | 17755.66 ns | 0.99 | 16059.24 ns |
| 100 on_next to 100 observers to publish_subject | 28890.05 ns | 19969.39 ns | 20388.50 ns | 0.98 | 22791.94 ns |
Scenarios
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| basic sample | 1409.76 ns | 13.39 ns | 13.38 ns | 1.00 | 18.94 ns |
| basic sample with immediate scheduler | 1432.75 ns | 5.60 ns | 5.59 ns | 1.00 | 12.05 ns |
| mix operators with disposables and without disposables | 6382.27 ns | 1439.21 ns | 1464.13 ns | 0.98 | 1482.32 ns |
| single disposable and looooooong indentity chain | 24157.51 ns | 1117.13 ns | 1074.46 ns | 1.04 | 3849.36 ns |
Aggregating Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+reduce(10, std::plus)+subscribe | 927.95 ns | 0.31 ns | 0.31 ns | 1.00 | 0.27 ns |
Error Handling Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| create(on_next(1), on_error())+on_error_resume_next(immediate_just(2)))+subscribe | 2068.33 ns | 981.99 ns | 1010.45 ns | 0.97 | 762.94 ns |
| create(on_error())+retry(1)+subscribe | 594.24 ns | 114.18 ns | 131.00 ns | 0.87 | 101.26 ns |
ci-macos
General
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| Subscribe empty callbacks to empty observable | 571.79 ns | 0.54 ns | 0.49 ns | 1.09 | 0.54 ns |
| Subscribe empty callbacks to empty observable via pipe operator | 402.51 ns | 0.57 ns | 0.50 ns | 1.13 | 0.51 ns |
Sources
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| from array of 1 - create + subscribe + immediate | 885.42 ns | 0.35 ns | 0.34 ns | 1.03 | 0.31 ns |
| from array of 1 - create + subscribe + current_thread | 1298.15 ns | 4.29 ns | 3.93 ns | 1.09 | 3.89 ns |
| concat_as_source of just(1 immediate) create + subscribe | 2941.94 ns | 210.73 ns | 183.10 ns | 1.15 | 233.43 ns |
| defer from array of 1 - defer + create + subscribe + immediate | 1011.85 ns | 0.47 ns | 0.36 ns | 1.33 | 0.35 ns |
| interval - interval + take(3) + subscribe + immediate | 2732.14 ns | 78.41 ns | 59.16 ns | 1.33 | 59.41 ns |
| interval - interval + take(3) + subscribe + current_thread | 3347.10 ns | 37.89 ns | 36.01 ns | 1.05 | 35.65 ns |
| from array of 1 - create + as_blocking + subscribe + new_thread | 47876.91 ns | 44243.04 ns | 26443.10 ns | 1.67 | 45123.09 ns |
| from array of 1000 - create + as_blocking + subscribe + new_thread | 50960.53 ns | 57382.26 ns | 31683.33 ns | 1.81 | 32998.50 ns |
| concat_as_source of just(1 immediate) and just(1,2 immediate)create + subscribe | 5422.33 ns | 294.29 ns | 200.32 ns | 1.47 | 210.45 ns |
Filtering Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take(1)+subscribe | 1246.78 ns | 0.51 ns | 0.35 ns | 1.46 | 0.42 ns |
| immediate_just+filter(true)+subscribe | 1338.47 ns | 0.45 ns | 0.46 ns | 0.99 | 0.39 ns |
| immediate_just(1,2)+skip(1)+subscribe | 1863.47 ns | 0.52 ns | 0.40 ns | 1.32 | 0.46 ns |
| immediate_just(1,1,2)+distinct_until_changed()+subscribe | 1098.92 ns | 0.47 ns | 0.37 ns | 1.27 | 0.37 ns |
| immediate_just(1,2)+first()+subscribe | 1545.41 ns | 0.54 ns | 0.39 ns | 1.38 | 0.39 ns |
| immediate_just(1,2)+last()+subscribe | 2264.80 ns | 1.08 ns | 1.01 ns | 1.07 | 1.07 ns |
| immediate_just+take_last(1)+subscribe | 2381.79 ns | 0.45 ns | 0.41 ns | 1.10 | 0.39 ns |
| immediate_just(1,2,3)+element_at(1)+subscribe | 972.95 ns | 0.52 ns | 0.37 ns | 1.38 | 0.34 ns |
Schedulers
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate scheduler create worker + schedule | 721.11 ns | 0.65 ns | 0.56 ns | 1.16 | 0.59 ns |
| current_thread scheduler create worker + schedule | 850.05 ns | 4.82 ns | 4.42 ns | 1.09 | 4.52 ns |
| current_thread scheduler create worker + schedule + recursive schedule | 826.22 ns | 73.07 ns | 71.56 ns | 1.02 | 76.40 ns |
Transforming Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+map(v*2)+subscribe | 963.18 ns | 4.52 ns | 3.23 ns | 1.40 | 3.30 ns |
| immediate_just+scan(10, std::plus)+subscribe | 2219.26 ns | 0.41 ns | 0.36 ns | 1.15 | 0.38 ns |
| immediate_just+flat_map(immediate_just(v*2))+subscribe | 4010.38 ns | 287.69 ns | 209.37 ns | 1.37 | 244.00 ns |
| immediate_just+buffer(2)+subscribe | 2561.78 ns | 16.43 ns | 17.13 ns | 0.96 | 22.40 ns |
| immediate_just+window(2)+subscribe + subscsribe inner | 2251.15 ns | 2574.25 ns | 1148.06 ns | 2.24 | 1475.93 ns |
Conditional Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take_while(false)+subscribe | 1152.60 ns | - | - | 0.00 | - |
| immediate_just+take_while(true)+subscribe | 1612.84 ns | 0.56 ns | 0.39 ns | 1.45 | 0.40 ns |
Utility Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(1)+subscribe_on(immediate)+subscribe | 2309.51 ns | 2.67 ns | 1.94 ns | 1.37 | 2.35 ns |
Combining Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(immediate_just(1), immediate_just(1)) + merge() + subscribe | 3205.01 ns | 296.66 ns | 230.48 ns | 1.29 | 339.87 ns |
| immediate_just(1) + merge_with(immediate_just(2)) + subscribe | 4211.93 ns | 241.60 ns | 207.75 ns | 1.16 | 248.22 ns |
| immediate_just(1) + with_latest_from(immediate_just(2)) + subscribe | - | 409.58 ns | 214.98 ns | 1.91 | 264.76 ns |
| immediate_just(immediate_just(1),immediate_just(1)) + switch_on_next() + subscribe | 3849.69 ns | 665.16 ns | 505.87 ns | 1.31 | 623.92 ns |
| immediate_just(1) + zip(immediate_just(2)) + subscribe | 3899.66 ns | 331.24 ns | 292.92 ns | 1.13 | 364.17 ns |
| immediate_just(immediate_just(1), immediate_just(1)) + concat() + subscribe | 3420.67 ns | 349.93 ns | 322.01 ns | 1.09 | 388.28 ns |
Subjects
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| publish_subject with 1 observer - on_next | 237.58 ns | 50.55 ns | 23.40 ns | 2.16 | 23.64 ns |
| subscribe 100 observers to publish_subject | 134709.00 ns | 138083.00 ns | 19641.53 ns | 7.03 | 16178.08 ns |
| 100 on_next to 100 observers to publish_subject | 80413.71 ns | 15805.80 ns | 12055.55 ns | 1.31 | 14686.93 ns |
Scenarios
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| basic sample | 1483.15 ns | 11.14 ns | 11.76 ns | 0.95 | 28.75 ns |
| basic sample with immediate scheduler | 2102.64 ns | 8.20 ns | 6.53 ns | 1.26 | 9.09 ns |
| mix operators with disposables and without disposables | 13245.16 ns | 1821.47 ns | 1547.47 ns | 1.18 | 1876.25 ns |
| single disposable and looooooong indentity chain | 25903.41 ns | 2300.42 ns | 2184.15 ns | 1.05 | 4515.64 ns |
Aggregating Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+reduce(10, std::plus)+subscribe | 1481.74 ns | 0.40 ns | 0.36 ns | 1.09 | 0.38 ns |
Error Handling Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| create(on_next(1), on_error())+on_error_resume_next(immediate_just(2)))+subscribe | 15109.76 ns | 3097.22 ns | 3500.00 ns | 0.88 | 3644.15 ns |
| create(on_error())+retry(1)+subscribe | 1415.15 ns | 209.67 ns | 204.52 ns | 1.03 | 195.08 ns |
ci-ubuntu-clang
General
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| Subscribe empty callbacks to empty observable | 165.36 ns | 0.31 ns | 0.65 ns | 0.47 | 1.56 ns |
| Subscribe empty callbacks to empty observable via pipe operator | 161.39 ns | 0.32 ns | 0.64 ns | 0.50 | 1.56 ns |
Sources
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| from array of 1 - create + subscribe + immediate | 316.92 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
| from array of 1 - create + subscribe + current_thread | 458.35 ns | 1.72 ns | 4.04 ns | 0.43 | 4.04 ns |
| concat_as_source of just(1 immediate) create + subscribe | 3062.09 ns | 170.18 ns | 130.66 ns | 1.30 | 132.14 ns |
| defer from array of 1 - defer + create + subscribe + immediate | 1043.66 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
| interval - interval + take(3) + subscribe + immediate | 3115.72 ns | 41.37 ns | 58.73 ns | 0.70 | 59.18 ns |
| interval - interval + take(3) + subscribe + current_thread | 4482.62 ns | 22.35 ns | 31.10 ns | 0.72 | 31.76 ns |
| from array of 1 - create + as_blocking + subscribe + new_thread | 14399.09 ns | 13593.06 ns | 35051.18 ns | 0.39 | 33765.67 ns |
| from array of 1000 - create + as_blocking + subscribe + new_thread | 17068.82 ns | 15701.75 ns | 41557.71 ns | 0.38 | 40605.72 ns |
| concat_as_source of just(1 immediate) and just(1,2 immediate)create + subscribe | 4481.38 ns | 201.49 ns | 148.81 ns | 1.35 | 149.13 ns |
Filtering Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take(1)+subscribe | 1514.64 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
| immediate_just+filter(true)+subscribe | 1181.12 ns | 0.23 ns | 0.31 ns | 0.73 | 0.31 ns |
| immediate_just(1,2)+skip(1)+subscribe | 1452.96 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
| immediate_just(1,1,2)+distinct_until_changed()+subscribe | 1157.33 ns | 0.22 ns | 0.32 ns | 0.70 | 0.33 ns |
| immediate_just(1,2)+first()+subscribe | 1822.41 ns | 0.22 ns | 0.31 ns | 0.72 | 0.31 ns |
| immediate_just(1,2)+last()+subscribe | 1354.40 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
| immediate_just+take_last(1)+subscribe | 1575.58 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
| immediate_just(1,2,3)+element_at(1)+subscribe | 1160.72 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
Schedulers
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate scheduler create worker + schedule | 411.95 ns | 0.44 ns | 1.55 ns | 0.28 | 0.64 ns |
| current_thread scheduler create worker + schedule | 499.17 ns | 1.87 ns | 4.04 ns | 0.46 | 4.05 ns |
| current_thread scheduler create worker + schedule + recursive schedule | 1157.67 ns | 36.94 ns | 57.71 ns | 0.64 | 65.62 ns |
Transforming Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+map(v*2)+subscribe | 1397.08 ns | 0.25 ns | 0.31 ns | 0.81 | 0.31 ns |
| immediate_just+scan(10, std::plus)+subscribe | 1314.12 ns | 0.30 ns | 0.62 ns | 0.48 | 0.33 ns |
| immediate_just+flat_map(immediate_just(v*2))+subscribe | 2916.33 ns | 183.34 ns | 139.26 ns | 1.32 | 137.02 ns |
| immediate_just+buffer(2)+subscribe | 1523.83 ns | 6.60 ns | 14.00 ns | 0.47 | 14.63 ns |
| immediate_just+window(2)+subscribe + subscsribe inner | 2678.01 ns | 1141.47 ns | 900.95 ns | 1.27 | 932.00 ns |
Conditional Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take_while(false)+subscribe | 2269.86 ns | - | - | 0.00 | - |
| immediate_just+take_while(true)+subscribe | 1453.29 ns | 0.38 ns | 0.31 ns | 1.22 | 0.31 ns |
Utility Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(1)+subscribe_on(immediate)+subscribe | 2641.92 ns | 0.22 ns | 0.31 ns | 0.72 | 0.31 ns |
Combining Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(immediate_just(1), immediate_just(1)) + merge() + subscribe | 4320.62 ns | 211.11 ns | 164.06 ns | 1.29 | 155.81 ns |
| immediate_just(1) + merge_with(immediate_just(2)) + subscribe | 5022.75 ns | 212.39 ns | 143.64 ns | 1.48 | 141.36 ns |
| immediate_just(1) + with_latest_from(immediate_just(2)) + subscribe | - | 193.24 ns | 143.37 ns | 1.35 | 138.79 ns |
| immediate_just(immediate_just(1),immediate_just(1)) + switch_on_next() + subscribe | 5871.55 ns | 596.81 ns | 381.48 ns | 1.56 | 377.88 ns |
| immediate_just(1) + zip(immediate_just(2)) + subscribe | 2874.70 ns | 258.08 ns | 216.27 ns | 1.19 | 203.35 ns |
| immediate_just(immediate_just(1), immediate_just(1)) + concat() + subscribe | 4213.97 ns | 314.16 ns | 226.57 ns | 1.39 | 224.93 ns |
Subjects
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| publish_subject with 1 observer - on_next | 71.34 ns | 29.47 ns | 19.24 ns | 1.53 | 22.93 ns |
| subscribe 100 observers to publish_subject | 238252.80 ns | 22566.11 ns | 17385.22 ns | 1.30 | 17479.69 ns |
| 100 on_next to 100 observers to publish_subject | 29699.29 ns | 14270.96 ns | 20315.86 ns | 0.70 | 20333.22 ns |
Scenarios
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| basic sample | 1645.67 ns | 7.42 ns | 11.51 ns | 0.64 | 20.84 ns |
| basic sample with immediate scheduler | 1634.10 ns | 2.21 ns | 5.91 ns | 0.37 | 6.53 ns |
| mix operators with disposables and without disposables | 7274.91 ns | 1304.95 ns | 1192.77 ns | 1.09 | 1491.58 ns |
| single disposable and looooooong indentity chain | 21304.48 ns | 1392.67 ns | 1250.01 ns | 1.11 | 4638.32 ns |
Aggregating Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+reduce(10, std::plus)+subscribe | 1371.14 ns | 0.22 ns | 0.31 ns | 0.71 | 0.31 ns |
Error Handling Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| create(on_next(1), on_error())+on_error_resume_next(immediate_just(2)))+subscribe | 2048.23 ns | 736.39 ns | 1183.77 ns | 0.62 | 1177.26 ns |
| create(on_error())+retry(1)+subscribe | 860.54 ns | 186.58 ns | 138.49 ns | 1.35 | 139.35 ns |
ci-windows
General
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| Subscribe empty callbacks to empty observable | 556.07 ns | 2.16 ns | 2.16 ns | 1.00 | 1.85 ns |
| Subscribe empty callbacks to empty observable via pipe operator | 572.10 ns | 2.16 ns | 2.16 ns | 1.00 | 1.85 ns |
Sources
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| from array of 1 - create + subscribe + immediate | 1155.57 ns | 4.93 ns | 4.94 ns | 1.00 | 5.55 ns |
| from array of 1 - create + subscribe + current_thread | 1419.82 ns | 15.75 ns | 15.74 ns | 1.00 | 15.45 ns |
| concat_as_source of just(1 immediate) create + subscribe | 3696.46 ns | 174.60 ns | 174.62 ns | 1.00 | 178.05 ns |
| defer from array of 1 - defer + create + subscribe + immediate | 1180.65 ns | 5.24 ns | 5.24 ns | 1.00 | 5.24 ns |
| interval - interval + take(3) + subscribe + immediate | 3736.36 ns | 139.81 ns | 139.74 ns | 1.00 | 142.29 ns |
| interval - interval + take(3) + subscribe + current_thread | 3460.30 ns | 60.15 ns | 59.86 ns | 1.00 | 62.80 ns |
| from array of 1 - create + as_blocking + subscribe + new_thread | 123175.00 ns | 118700.00 ns | 120400.00 ns | 0.99 | 119788.89 ns |
| from array of 1000 - create + as_blocking + subscribe + new_thread | 132655.56 ns | 135675.00 ns | 136975.00 ns | 0.99 | 137275.00 ns |
| concat_as_source of just(1 immediate) and just(1,2 immediate)create + subscribe | 5329.67 ns | 206.57 ns | 201.39 ns | 1.03 | 215.13 ns |
Filtering Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take(1)+subscribe | 1818.86 ns | 19.42 ns | 19.42 ns | 1.00 | 21.36 ns |
| immediate_just+filter(true)+subscribe | 1610.58 ns | 18.50 ns | 18.51 ns | 1.00 | 21.59 ns |
| immediate_just(1,2)+skip(1)+subscribe | 2008.80 ns | 17.89 ns | 17.91 ns | 1.00 | 21.60 ns |
| immediate_just(1,1,2)+distinct_until_changed()+subscribe | 1328.95 ns | 20.67 ns | 20.68 ns | 1.00 | 26.86 ns |
| immediate_just(1,2)+first()+subscribe | 2372.16 ns | 18.20 ns | 18.21 ns | 1.00 | 19.43 ns |
| immediate_just(1,2)+last()+subscribe | 1464.98 ns | 19.13 ns | 19.15 ns | 1.00 | 22.84 ns |
| immediate_just+take_last(1)+subscribe | 2023.02 ns | 64.85 ns | 64.93 ns | 1.00 | 70.17 ns |
| immediate_just(1,2,3)+element_at(1)+subscribe | 1622.12 ns | 20.97 ns | 20.98 ns | 1.00 | 21.63 ns |
Schedulers
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate scheduler create worker + schedule | 477.39 ns | 4.32 ns | 4.32 ns | 1.00 | 4.32 ns |
| current_thread scheduler create worker + schedule | 648.68 ns | 11.11 ns | 11.11 ns | 1.00 | 11.11 ns |
| current_thread scheduler create worker + schedule + recursive schedule | 1079.50 ns | 103.96 ns | 101.36 ns | 1.03 | 103.12 ns |
Transforming Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+map(v*2)+subscribe | 1328.23 ns | 18.80 ns | 18.80 ns | 1.00 | 21.63 ns |
| immediate_just+scan(10, std::plus)+subscribe | 1422.12 ns | 20.96 ns | 20.96 ns | 1.00 | 23.80 ns |
| immediate_just+flat_map(immediate_just(v*2))+subscribe | 3837.33 ns | 186.30 ns | 185.11 ns | 1.01 | 223.46 ns |
| immediate_just+buffer(2)+subscribe | 2291.50 ns | 64.26 ns | 63.51 ns | 1.01 | 72.33 ns |
| immediate_just+window(2)+subscribe + subscsribe inner | 3974.07 ns | 1216.92 ns | 1192.15 ns | 1.02 | 1226.57 ns |
Conditional Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+take_while(false)+subscribe | 1305.28 ns | 17.57 ns | 17.57 ns | 1.00 | 19.13 ns |
| immediate_just+take_while(true)+subscribe | 1329.92 ns | 18.50 ns | 18.50 ns | 1.00 | 21.60 ns |
Utility Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(1)+subscribe_on(immediate)+subscribe | 3205.02 ns | 11.10 ns | 11.11 ns | 1.00 | 11.11 ns |
Combining Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just(immediate_just(1), immediate_just(1)) + merge() + subscribe | 5054.46 ns | 197.43 ns | 195.27 ns | 1.01 | 222.13 ns |
| immediate_just(1) + merge_with(immediate_just(2)) + subscribe | 5710.11 ns | 186.78 ns | 178.89 ns | 1.04 | 204.81 ns |
| immediate_just(1) + with_latest_from(immediate_just(2)) + subscribe | - | 195.15 ns | 194.65 ns | 1.00 | 199.56 ns |
| immediate_just(immediate_just(1),immediate_just(1)) + switch_on_next() + subscribe | 6119.16 ns | 444.60 ns | 443.65 ns | 1.00 | 489.05 ns |
| immediate_just(1) + zip(immediate_just(2)) + subscribe | 3865.37 ns | 538.25 ns | 519.56 ns | 1.04 | 516.04 ns |
| immediate_just(immediate_just(1), immediate_just(1)) + concat() + subscribe | 4853.97 ns | 314.42 ns | 314.38 ns | 1.00 | 325.14 ns |
Subjects
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| publish_subject with 1 observer - on_next | 36.72 ns | 29.42 ns | 29.27 ns | 1.01 | 30.01 ns |
| subscribe 100 observers to publish_subject | 264250.00 ns | 25013.33 ns | 25450.00 ns | 0.98 | 24830.23 ns |
| 100 on_next to 100 observers to publish_subject | 51881.82 ns | 35939.39 ns | 35922.58 ns | 1.00 | 32997.22 ns |
Scenarios
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| basic sample | 1853.25 ns | 96.57 ns | 96.80 ns | 1.00 | 112.11 ns |
| basic sample with immediate scheduler | 1857.89 ns | 66.73 ns | 68.51 ns | 0.97 | 82.35 ns |
| mix operators with disposables and without disposables | 9381.75 ns | 1787.22 ns | 1797.99 ns | 0.99 | 2439.19 ns |
| single disposable and looooooong indentity chain | 25641.46 ns | 1655.18 ns | 1630.37 ns | 1.02 | 6267.21 ns |
Aggregating Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| immediate_just+reduce(10, std::plus)+subscribe | 1458.16 ns | 19.11 ns | 19.11 ns | 1.00 | 22.83 ns |
Error Handling Operators
| name | rxcpp | rpp | prev rpp | ratio | rpp no optimization |
|---|---|---|---|---|---|
| create(on_next(1), on_error())+on_error_resume_next(immediate_just(2)))+subscribe | 1896.36 ns | 357.24 ns | 353.06 ns | 1.01 | 380.86 ns |
| create(on_error())+retry(1)+subscribe | 1721.11 ns | 138.15 ns | 137.92 ns | 1.00 | 139.85 ns |
Fixes #762.
client_bidi_reactor::init()andclient_read_reactor::init()callStartCall()and then post the firstStartRead()from the caller's thread. If the call ends at once (connection refused, deadline, cancelled context), gRPC can finish it on its own threads right afterStartCall()and runOnDone(), which doesdelete this, whileinit()is still running. The read then runs on a freed reactor or on a call that has already ended.Change.
init()posts the first read beforeStartCall(). The gRPC callback API allows this (client_callback.h at v1.51.1):With this order
init()starts no operation afterStartCall(), so no hold is needed (L291-L293):AddHold()/RemoveHold()aroundinit()would also be correct. I chose the reorder because it needs no extra calls or state.client_write_reactor::init()only callsStartCall(), so it has no such window. Writes are a related case. For the bidi and write reactors, the doc comments and the doxygen example now emit the first value beforeinit(); gRPC posts it until the call starts, and the existing tests already use this order. A write posted afterinit()from outside a reaction still needs a hold under the gRPC rules (L301-L312). Adding one would change whenOnDonecan run, so it is not part of this PR.Test. New case in
test_async_client.cpp, "async client reactor: call that ends during init()": 300 calls per reactor type on the in-process channel, each with its context cancelled beforeinit(). Results with AddressSanitizer (GCC 14.2, gRPC 1.51.1, Debian trixie):The rest of
test_async_clientpasses as well. I built these tests against the system gRPC rather than through the conan setup, and did not run the repository's CI presets.🤖 Generated with Claude Code
Summary by CodeRabbit