Repository navigation
feat: add input port update subscriptions - #1247
Conversation
|
Could a maintainer please re-run the failed Linux/Pixi job in this workflow run? The build was interrupted with exit code 143 and "The runner has received a shutdown signal." The Windows/Pixi job passed. I tried to re-run the failed job, but GitHub rejected the request because I do not have the required repository permissions. Thank you! |
|
Thanks @kVinsom, this is a carefully built proposal: the documented contract, re-entrancy handling and 15 focused tests are appreciated. We can't take it into 4.x, though, for two reasons:
For the use case in #872 (react when the "topic" input changes), polling at tick time with As you suggested yourself, this fits BT.CPP 5 better. If you want to keep it alive for v5, a fast path when nobody subscribes (e.g. an atomic subscriber count checked before any locking) and a small write-throughput benchmark would make it much easier to evaluate. I'll leave the PR open with that in mind. (The red |
…x ABI Blackboard::create() is inline, so client binaries bake in sizeof(Blackboard): the per-key update_signals_ map broke the ABI of the 4.x series. It also made every write take storage_mutex_ exclusively and look the key up again, even without subscribers, and set() could notify the signal of a different key resolution than the one it wrote to. The subscribers are now kept in a shared_ptr appended as the last member of Blackboard::Entry. Entries are allocated only inside the library and inline code only touches the earlier members, whose offsets do not change; Blackboard is byte-identical to before. The pointer is guarded by entry_mutex and created lazily on the first subscription. Writers copy it under the entry lock they already hold and notify after releasing it, so a write without subscribers costs a null check. subscribeToEntry() follows remapping and '@' via getEntry() and, when the entry is missing, creates a generic one (subscribeToInputPort() uses the port's type, like tree creation does). As a consequence a subscription is bound to the entry: unset() drops it and a re-created entry is not observed. Writes from code compiled against older headers do not notify. Both are documented and covered by tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Update to my earlier comment: we found a way to make this ABI-compatible with 4.x, so I reworked it on top of your branch (thanks for enabling maintainer edits; your commit is unchanged). Two commits: a merge of master, and 1c37445. What changed:
Semantics that differ from your version (documented in
Your tests are kept, with a few adapted to (1), plus new ones: the subscription is bound to the entry, and writes without subscribers don't allocate a signal. Locally: full suite green, ASan/UBSan and TSan clean on the callback and blackboard tests, pre-commit clean. Comments welcome. I'd like your 👍 before this gets merged. |
Input-port consumers currently have to poll during a tick to discover blackboard writes. This draft adds instance-level
TreeNode::subscribeToInputPort()so a node can react before its next tick, without putting instance callbacks into staticprovidedPorts()declarations.Related to #872. Design proposal for BT.CPP 5: this adds storage to
Blackboard, so it changes its binary layout and requires rebuilding clients. The draft targetsmasterfor discussion because there is no public v5 branch. Please advise on the preferred target/API before merging.Behavior
{=}, and blackboard-backed defaults. Constant inputs return an empty handle.set(),setOutput(), script assignments, JSON import, and snapshot restoration. Subscribe-before-first-write does not create or type-lock an entry; subscriptions survive removal and recreation of the key.Signalis unchanged.Contract to review
This observes successful writes, including equal values, rather than comparing arbitrary
Anyvalues. Callbacks read the latest value; concurrent writers can overlap callbacks and do not guarantee delivery order. Resetting a handle does not wait for callbacks already selected for invocation: callers must stop writers before destroying captured targets. Exceptions propagate after the write commits and stop the remaining callbacks in that notification.Raw
Entry/AnyPtrLockedwrites require an explicitnotifyEntryUpdated()after unlocking. Remapping is resolved at registration; changing it requires a new subscription. Subscription records remain associated with keys for the blackboard lifetime.Validation
./run_clang_tidy.sh: passed.tests/test_helper.hpp(member initialization and special members) andtests/gtest_loggers.cpp(reinterpret cast).Tests are run serially because the existing logger tests share and delete
/tmp/bt_logger_test; a parallel trial produced file/database collisions. macOS, Windows and ROS builds have not been run locally.Developed with assistance from OpenAI Codex.