Repository navigation
feat(rtps): expose discovery timing, task, pool and domain-id settings as menuconfig knobs - #873
Conversation
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
4 open issues
The section header says these are “pure capacity caps”, butDOMAIN_IDis introduced immediately… · New The comment says the engine/domain raises anySPDP_WRITER_STACKSIZEbelow 4096 up to 4096, but… · New This comment is now inaccurate/misleading:RTPS_LIMIT_OVERRIDESis expanded to cover non-capacity… · New The host-side override validation now enforces a 2048-byte minimum for… · New
What changed in this PR
Exposes additional RTPS profile “constants” (discovery/reliability timing, task priorities/stack sizes, locator caps, and domain id) as build-time knobs via ESP-IDF menuconfig and the host build’s RTPS_LIMIT_OVERRIDES, without changing defaults or wire format.
Changes:
- Added new Kconfig options and CMake plumbing to map
CONFIG_RTPS_*toRTPS_CFG_*compile definitions. - Extended host-build override parsing/validation (
RTPS_LIMIT_OVERRIDES) to accept the new tuning and domain-id constants. - Updated profile headers and documentation to support the new
RTPS_CFG_*override blocks.
| File | Description |
|---|---|
| lib/espp.cmake | Extends override mechanism beyond capacity caps to timing/tasks/domain id; adds per-type validation. |
| components/rtps/CMakeLists.txt | Wires new Kconfig knobs into RTPS_CFG_* compile definitions and adds basic validation. |
| components/rtps/Kconfig | Adds menuconfig knobs for discovery timing, task settings, domain id, and extra capacity caps. |
| components/rtps/include/rtps/config_desktop.hpp | Converts several fixed constants (domain id/timing/stacks/etc.) to RTPS_CFG_* overridable defaults. |
| components/rtps/include/rtps/config_host_large.hpp | Same as above for host_large profile. |
| components/rtps/include/rtps/config_esp32.hpp | Same as above for esp32 profile (ESP-IDF). |
| components/rtps/include/rtps_participant.hpp | Updates limitations doc to reflect build-time configurability via new knobs. |
| components/rtps/README.md | Documents new configuration knobs and clarifies override mechanism scope. |
Review effort: Lite
|
✅Static analysis result - no issues found! ✅ |
…s as menuconfig knobs Every profile constant the engine actually reads is now overridable the way #870 made the capacity caps: an RTPS_CFG_<NAME> default block in the three profile headers (values unchanged), a Kconfig entry (0 = profile default), the component CMake mapping, and the same names in the host build's RTPS_LIMIT_OVERRIDES list with per-type range checks. - Custom capacity overrides: SPDP_MAX_NUM_LOCATORS. - New menu "Discovery and reliability timing (advanced)": SPDP_RESEND_PERIOD_MS, SPDP_CYCLECOUNT_HEARTBEAT, SF_WRITER_HB_PERIOD_MS, SPDP_DEFAULT_REMOTE_LEASE_DURATION_S (the lease we announce and the default assumed for a peer without one), SPDP_MAX_REMOTE_LEASE_DURATION_S - all in whole seconds / ms; the help texts state the relations that must hold between them. - "Transport task priority" becomes "Tasks (advanced)": the existing THREAD_POOL_READER_PRIO plus THREAD_POOL_READER_STACKSIZE, SPDP_WRITER_PRIO, SPDP_WRITER_STACKSIZE (stacks below 2048 refused). - RTPS_DOMAIN_ID (0..232): 0 is a real value, always passed through; the port base and GUID prefix byte 9 derive from it. README table and the facade header's compile-time note updated. Constants nothing reads (SPDP_LEASE_DURATION, THREAD_POOL_NUM_*, THREAD_POOL_WORKLOAD_QUEUE_LENGTH_*, HEARTBEAT_STACKSIZE, THREAD_POOL_WRITER_PRIO/STACKSIZE, OVERALL_HEAP_SIZE) are deliberately not exposed. No wire-format or default change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU
- The profile headers' preface and lib/espp.cmake no longer call the overrides "capacity-only": they describe the four groups (capacity caps, discovery/reliability timing, task scheduling, domain id), none of which changes the wire format, and what the domain id derives. - The SPDP_WRITER_STACKSIZE comment states exactly where the 4096 clamp lives (Domain::initializeTransport(), every target) and that espp::Task applies stack sizes only on ESP via esp_pthread, so on a host build both stack knobs have no effect (std::thread's platform default). - The host override validation no longer imposes the FreeRTOS 2048-byte stack minimum (the host profiles' own 1600 / 550 defaults could not be re-stated as overrides); it is enforced by the ESP-IDF component build only, where it is real. Host: type range only, with the reason noted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU
|
Rebased onto main now that #870 and #871 are merged; the PR no longer carries #870's diff. Three conflicts, all one cause: the merged #870 widened Re-verified after the rebase: host library builds, 🤖 Generated with Claude Code |
a456834 to
7c92122
Compare
Audit of every rtps::Config constant against its engine use sites. Nine constants had no reader anywhere (components/rtps, lib/, pc/, docs, Kconfig, CMake): HEARTBEAT_STACKSIZE, THREAD_POOL_WRITER_STACKSIZE, THREAD_POOL_NUM_WRITERS, THREAD_POOL_NUM_READERS, THREAD_POOL_WRITER_PRIO, THREAD_POOL_WORKLOAD_QUEUE_LENGTH_USERTRAFFIC/_METATRAFFIC, OVERALL_HEAP_SIZE (which only summed the dead ones) and SPDP_LEASE_DURATION (esp32 profile only; the announced lease is SPDP_DEFAULT_REMOTE_LEASE_DURATION). They are leftovers of the upstream thread-per-writer design: this engine runs the transport worker pool + reactor and one protocol task, nothing else. Removed from all three profiles. IP_ADDRESS was read only by the two-argument Participant::reuse(), which has no caller (Domain always passes the real interface address through the four-argument overload). Both the overload and the constant are gone. VENDOR_ID and BASE_GUID_PREFIX stay and now say why: VENDOR_ID is wire identity (message header, SPDP, GUID prefix bytes 7-8); on esp32 BASE_GUID_PREFIX is only the fallback used when neither MAC can be read, with bytes 6-9 always overwritten by participant id / vendor / domain. ParticipantProxyData::isAlive() now widens before scaling: the remote lease is peer-supplied int32 seconds and `seconds * 1000` overflowed int32 above ~24.8 days (a negative lease now counts as zero instead of wrapping to a huge unsigned value). No default value or wire format changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU
Config constant audit (every
|
| Constant | Type | Value | Use sites | Verdict |
|---|---|---|---|---|
VENDOR_ID |
VendorId_t |
{13,37} all | MessageFactory.hpp:51 (header), SPDPAgent.cpp:243,272 (announce), Domain.cpp:967,993 (GUID prefix bytes 7-8) |
OK, wire identity. Kept, comment added. |
IP_ADDRESS |
std::array<uint8_t,4> |
{192,168,4,1} all | only the 2-arg Participant::reuse(), which has no caller (Domain.cpp:344 uses the 4-arg overload with the real interface address) |
Dead, removed together with the overload. The desktop comment ("set in lwipcfg.h too") was stale. |
BASE_GUID_PREFIX |
GuidPrefix_t |
fixed / GUID_RANDOM / GUID_RANDOM |
Domain.cpp:976-986 only on the non-MAC path; on ESP the Ethernet/Wi-Fi MAC path returns first; bytes 6-9 always overwritten (id, vendor, domain) |
OK. Deliberate profile difference; esp32 comment now says it is only the no-MAC fallback and that two fallback boards collide. |
DOMAIN_ID k |
uint8_t |
0 all | udpUtils.hpp:51-92 (port math 7400 + 250*id + ...), Domain.cpp:531 (dedicated port base), Domain.cpp:969,995 (prefix byte 9), 2 pc tests |
OK. Kconfig/host max 232 matches the UDP port budget; the sentinel is not used for it (always defined), so a real 0 is fine. |
MAX_SAMPLE_SIZE |
DataSize_t |
256 KB / 8 MB / 8 MB | Reader.cpp:75-77 (drop fragmented sample > max) |
OK, fed from RTPS_MAX_SAMPLE_SIZE (CMake per profile). |
NUM_STATELESS_WRITERS/READERS k |
uint8_t |
5 / 16 / 64 | Domain.hpp:209-212 std::array pools; rtps_participant.cpp:316-393 messages |
OK. CMake cross-check (>= participants, 1 SPDP W/R each) matches Domain. |
NUM_STATEFUL_WRITERS/READERS k |
uint8_t |
5 / 32 / 128 | Domain.hpp:209-212; messages |
OK. Cross-check (>= 2 x participants for SEDP) matches. |
MAX_NUM_PARTICIPANTS k |
uint8_t |
1 / 8 / 32 | Domain.hpp:123 array, Domain.cpp:363,378; m_numParticipants is uint8_t |
OK (255 cap keeps the counter from wrapping). |
NUM_WRITERS/READERS_PER_PARTICIPANT k |
uint8_t |
10 / 16 / 64 | Participant.hpp:133-134 arrays; messages (3 builtins each) |
OK; CMake minimum 3 cites the real builtin count. |
NUM_WRITER_PROXIES_PER_READER / NUM_READER_PROXIES_PER_WRITER k |
uint8_t |
6 / 8 / 16 | Reader.hpp:178, Writer.hpp:160 MemoryPool (uint32 SIZE, uint32 bit index) |
OK. |
MAX_NUM_UNMATCHED_REMOTE_WRITERS/READERS k |
uint8_t / uint16_t / uint16_t |
50,50 / 256,128 / 1024,512 | SEDPAgent.hpp:86,89 MemoryPool |
OK; widening is deliberate (#870) and espp.cmake RTPS_LIMIT_UINT16_KNOBS allows 65535 only off embedded. |
MAX_NUM_READER_CALLBACKS k |
uint8_t |
5 / 8 / 16 | Reader.hpp:205 array |
OK. |
HISTORY_SIZE_STATELESS / _STATEFUL k |
uint8_t |
2,10 / 8,16 / 32,32 | StatelessWriter.hpp:68, StatefulWriter.hpp:82 (template <uint16_t SIZE>) |
OK (uint8 into uint16 template). |
MAX_TYPENAME_LENGTH / MAX_TOPICNAME_LENGTH k |
uint8_t |
64 all | TopicData.hpp:49-50 char arrays, :128; TopicData.cpp:113,130 receive (len > MAX reject, len includes the CDR NUL), :236,250 serialize (strlen+1); create path Domain.cpp:738-745,822-829 and rtps_participant.cpp:302-307 reject strlen >= MAX |
OK and consistent (63 chars + NUL both ways). See open question 1. |
THREAD_POOL_READER_STACKSIZE k |
int |
6144 / 1600 / 1600 | EsppTransport.cpp:90,99 worker pool + reactor stack_size_bytes |
OK. Bytes; applied on ESP only (esp_pthread); host std::thread ignores it. Kconfig 0..65535, CMake FATAL below 2048. |
SPDP_WRITER_STACKSIZE k |
uint16_t |
4096 / 550 / 550 | Domain.cpp:104 (> 4096 ? v : 4096) protocol task |
OK; comment states the clamp and the ESP-only effect. uint16 matches the 65535 Kconfig/host max. |
SF_WRITER_HB_PERIOD_MS k |
uint16_t |
4000 / 2000 / 2000 | StatefulWriter.cpp:588-590 as std::chrono::milliseconds (/4 while unacked) |
OK, ms. Kconfig/host 0..65535 = uint16. |
SPDP_RESEND_PERIOD_MS k |
uint16_t |
2000 / 1000 / 1000 | Domain.cpp:158 ms cadence of the protocol task |
OK, ms. |
SPDP_CYCLECOUNT_HEARTBEAT k |
uint8_t |
2 all | SPDPAgent.cpp:68 (m_cycleHB == count, m_cycleHB is uint8_t from 0) |
OK: liveliness checked every (count+1) announce rounds, as the comment says; 255 cannot overflow the counter. See open question 2. |
SPDP_WRITER_PRIO k |
uint8_t |
5 / 3 / 3 | Domain.cpp:105 |
OK. Kconfig 0..24 = FreeRTOS configMAX_PRIORITIES-1; the 0 sentinel only hides idle priority. |
THREAD_POOL_READER_PRIO k |
int |
5 / 3 / 3 | EsppTransport.cpp:91,100 |
OK, same range. Comment pointed at a non-existent "Transport task priority" menu; now "RTPS -> Tasks". |
SPDP_MAX_NUMBER_FOUND_PARTICIPANTS k |
uint8_t / uint16_t / uint16_t |
10 / 32 / 128 | Participant.hpp:137 MemoryPool |
OK (#870 widening). |
SPDP_MAX_NUM_LOCATORS k |
uint8_t |
1 / 8 / 16 | ParticipantProxyData.hpp:59-62,90, .cpp:49,227, pc/tests/rtps_locator_subnet.cpp |
OK; loop index is int. Comment's 4 lists x 12 bytes per slot is right. |
SPDP_DEFAULT_REMOTE_LEASE_DURATION k (_S) |
Duration_t |
{5,0} / {100,0} / {100,0} | ParticipantProxyData.hpp:64 proxy default, SPDPAgent.cpp:289-290 our own announcement |
OK: {seconds, fraction=0} from the knob; Kconfig/host 0..3600 fits int32 seconds. Relation "> resend period" holds in every profile (5 s vs 2 s, 100 s vs 1 s). |
SPDP_MAX_REMOTE_LEASE_DURATION k (_S) |
Duration_t |
{90,0} / {180,0} / {180,0} | ParticipantProxyData.hpp:isAlive() min(remote lease, this) in ms |
Value OK (default <= max in every profile). The ms conversion did int32 seconds * 1000 on the peer-supplied lease: UB above ~24.8 days, and a negative lease wrapped to a huge unsigned value. Fixed: widened to uint64, negative counts as 0. |
MAX_NUM_UDP_CONNECTIONS k |
int |
10 / 24 / 72 | EsppTransport.hpp:168 std::array<Channel, N> |
OK; CMake minimum 4 and the 2 + 2*participants cross-check match Domain. |
SPDP_LEASE_DURATION |
Duration_t |
{5,0} esp32 only | none | Dead, removed (the announced lease is SPDP_DEFAULT_REMOTE_LEASE_DURATION). |
HEARTBEAT_STACKSIZE |
int |
6144 / 1200 / 1200 | none | Dead, removed (heartbeats run on the protocol task). |
THREAD_POOL_WRITER_STACKSIZE |
int |
4096 / 1100 / 1100 | none | Dead, removed (writers send inline; no writer tasks). |
THREAD_POOL_NUM_WRITERS / _NUM_READERS |
int |
2 all | none | Dead, removed (pool depth is fixed in EsppTransport). |
THREAD_POOL_WRITER_PRIO |
int |
5 / 3 / 3 | none | Dead, removed. |
THREAD_POOL_WORKLOAD_QUEUE_LENGTH_USERTRAFFIC / _METATRAFFIC |
int |
60 / 32 / 64 | none | Dead, removed (EsppTransport sets max_queue_size = 64 itself). |
OVERALL_HEAP_SIZE |
constexpr int |
derived | none | Dead, removed (summed only the dead stack constants). |
Knob plumbing checks that passed: every Kconfig range fits the C++ type of the profile it applies to (uint8 knobs 0..255, uint16 0..65535, the two uint16-on-host registries 65535 only off embedded, seconds 0..3600 into int32, priorities 0..24, DOMAIN_ID 0..232); lib/espp.cmake applies the same per-type maxima; no exposed knob has a legitimate value of 0 that the 0 = profile default sentinel could shadow (DOMAIN_ID is always defined, so domain 0 works; priority 0 is the idle task; a 0 lease/period/cycle count is never sensible).
Fixes in 17ddebc
- Removed the nine dead constants above from all three profiles (and
IP_ADDRESSplus its only reader, the uncalled 2-argParticipant::reuse). - Added a "Tasks" preface in each profile explaining that the engine runs exactly two kinds of task, so readers stop looking for heartbeat/writer task knobs.
VENDOR_ID/BASE_GUID_PREFIXcomments (wire identity; esp32 fallback semantics);THREAD_POOL_READER_PRIOcomment now names the real menu.ParticipantProxyData::isAlive()lease-to-ms arithmetic widened (see table).
Verification
- Host lib (
lib/, default host profile) +rtps_golden,rtps_locator_subnet,rtps_sedp_dedicated_locator,rtps_pubsub: all PASS. The only host warnings are the pre-existing-Winconsistent-missing-overrideones inStatelessWriter.hpp/StatefulWriter.hpp(untouched). - Host configure with
RTPS_LIMIT_OVERRIDES="SPDP_MAX_NUM_LOCATORS=4;SPDP_RESEND_PERIOD_MS=500;DOMAIN_ID=3;SPDP_MAX_REMOTE_LEASE_DURATION_S=30;THREAD_POOL_READER_PRIO=7": accepted, each override echoed. - esp32
components/rtps/example(IDF 6.1, component manager off) withCONFIG_RTPS_DOMAIN_ID=5,CONFIG_RTPS_SPDP_DEFAULT_REMOTE_LEASE_DURATION_S=20,CONFIG_RTPS_LIMIT_SPDP_MAX_NUM_LOCATORS=4,CONFIG_RTPS_LIMIT_SPDP_MAX_NUMBER_FOUND_PARTICIPANTS=24,CONFIG_RTPS_SPDP_WRITER_PRIO=6: builds; all fiveRTPS_CFG_*definitions reach the compiler (checked incompile_commands.json); no warnings fromcomponents/rtps. - clang-format (pre-commit) clean on the touched files.
Open questions (not changed)
TopicData::readFromBufferaccepts a name of exactlyMAX_*NAME_LENGTHbytes. Per CDR the length includes the NUL, so a conforming peer can never leave the array unterminated, but a non-conforming one could (andmatchesTopicOfusesstrcmp). Forcingname[len-1] = '\0'afterreadByteswould be a cheap hardening; left alone because it is not a config issue.SPDP_CYCLECOUNT_HEARTBEAT=0(liveliness check every announce round) is a legitimate value the0 = profile defaultsentinel hides. Nobody is likely to want it, but if so the Kconfig would need a-1-style sentinel.THREAD_POOL_READER_STACKSIZEisintwhileSPDP_WRITER_STACKSIZEisuint16_t; both have a 65535 ceiling in Kconfig/host validation, so it is cosmetic. Harmonising them (bothuint16_t, or bothint) would be a type change on a public header, so I left it for a separate decision.
🤖 Generated with Claude Code

Summary
Follow-up to #870, which added the last capacity cap to the override mechanism. This exposes the remaining profile constants the engine actually reads - discovery and reliability timing, the task priorities and stacks, one more capacity cap, and the domain id - as menuconfig knobs with the same
RTPS_CFG_<NAME>pattern, and accepts the same names in the host build's-DRTPS_LIMIT_OVERRIDESlist. No default changes, no wire-format change: these alter only when discovery and reliability messages are sent and how the engine's tasks are scheduled.Rebased onto main after #870 merged (its constant kept as merged: uint16_t on the host profiles).
New knobs
0 = profile defaultfor all but the domain id, where 0 is the DDS default domain.RTPS -> Domain id(RTPS_DOMAIN_ID, 0..232)DOMAIN_IDROS_DOMAIN_IDequivalent). The port base7400 + 250 * idand GUID prefix byte 9 derive from it, so every participant on a domain must use the same value. Compile-time, shared by all participants in the firmware.RTPS_LIMIT_SPDP_MAX_NUM_LOCATORSSPDP_MAX_NUM_LOCATORSSPDP_MAX_NUMBER_FOUND_PARTICIPANTS). 1 already reaches a multi-homed peer since #866 prefers the locator on our network.RTPS_SPDP_RESEND_PERIOD_MSSPDP_RESEND_PERIOD_MSRTPS_SPDP_CYCLECOUNT_HEARTBEATSPDP_CYCLECOUNT_HEARTBEAT(n + 1)announce rounds.RTPS_SF_WRITER_HB_PERIOD_MSSF_WRITER_HB_PERIOD_MSRTPS_SPDP_DEFAULT_REMOTE_LEASE_DURATION_SSPDP_DEFAULT_REMOTE_LEASE_DURATION(seconds)RTPS_SPDP_MAX_REMOTE_LEASE_DURATION_SSPDP_MAX_REMOTE_LEASE_DURATION(seconds)min(its lease, this).RTPS_THREAD_POOL_READER_PRIOTHREAD_POOL_READER_PRIORTPS_THREAD_POOL_READER_STACKSIZETHREAD_POOL_READER_STACKSIZERTPS_SPDP_WRITER_PRIOSPDP_WRITER_PRIOrtps_protocol: announcements, HEARTBEATs, liveliness).RTPS_SPDP_WRITER_STACKSIZESPDP_WRITER_STACKSIZEThe help texts state the relations that must hold: the announced lease must comfortably exceed the announcement period (peers drop a participant whose announcements they miss for a whole lease; 2-3 announcements per lease); the liveliness check runs every
(SPDP_CYCLECOUNT_HEARTBEAT + 1) * SPDP_RESEND_PERIOD_MS, so a vanished peer lingers up to that long past its lease; a remote lease cap below a peer's announcement period drops that peer while it is alive.Mechanics
#ifndef RTPS_CFG_Xdefault block, values unchanged. The two leases are expressed in whole seconds ({RTPS_CFG_..._S, 0}).components/rtps/CMakeLists.txt:SPDP_MAX_NUM_LOCATORSjoinsRTPS_LIMIT_KNOBS; a newRTPS_TUNING_KNOBSloop mapsCONFIG_RTPS_<knob>(nonzero) toRTPS_CFG_<knob>and subsumes the previousTHREAD_POOL_READER_PRIOspecial case;RTPS_DOMAIN_IDis always passed through (0 is a real value).lib/espp.cmake: the names are accepted inRTPS_LIMIT_OVERRIDESwith per-type ranges (uint8, uint16 on every profile, int stacks, lease seconds, domain id 0..232) and the stack minimum;THREAD_POOL_READER_PRIObecomes host-overridable too.Deliberately not exposed
SPDP_LEASE_DURATION,THREAD_POOL_NUM_WRITERS/READERS,THREAD_POOL_WORKLOAD_QUEUE_LENGTH_*,HEARTBEAT_STACKSIZE,THREAD_POOL_WRITER_PRIO/STACKSIZEandOVERALL_HEAP_SIZEare defined in the profile headers but nothing in the engine reads them (leftovers of the original per-writer threads). A knob for them would do nothing; they are left as they are and could be removed in a cleanup.Test plan
components/rtps/examplebuilds for esp32 (IDF 6.1, component manager off) with defaults; every newCONFIG_RTPS_*symbol is generated at 0 (domain id 0)CONFIG_RTPS_DOMAIN_ID=5,CONFIG_RTPS_SPDP_DEFAULT_REMOTE_LEASE_DURATION_S=20,CONFIG_RTPS_THREAD_POOL_READER_STACKSIZE=8192,CONFIG_RTPS_LIMIT_SPDP_MAX_NUM_LOCATORS=4,CONFIG_RTPS_SPDP_RESEND_PERIOD_MS=500,CONFIG_RTPS_SPDP_WRITER_PRIO=7: builds, andcompile_commands.jsonshows the sixRTPS_CFG_*=definitions on every rtps source AND on the example's own translation unitlib, host profile) with no overrides, installed;pctestsrtps_golden(byte-identical vectors),rtps_locator_subnet,rtps_sedp_dedicated_locator,rtps_pubsubpass-DRTPS_LIMIT_OVERRIDES="SPDP_MAX_NUM_LOCATORS=4;SPDP_RESEND_PERIOD_MS=500;DOMAIN_ID=3;THREAD_POOL_READER_STACKSIZE=65536"configures (each override reported) and builds;DOMAIN_ID=300andSPDP_WRITER_STACKSIZE=1024are refused as out of range🤖 Generated with Claude Code
https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU