[ntcore] Add PubSubOption to disable handle signaling (#8972)

This helps reduce lock contention on Synchronization Signal objects if
the user does not intend to use this signaling path.

Use this option on the RobotBase MultiSubscriber to all topics.
This commit is contained in:
Peter Johnson
2026-06-19 17:17:37 -05:00
committed by GitHub
parent 8d2f3212e7
commit 608f024f82
14 changed files with 130 additions and 13 deletions

View File

@@ -20,6 +20,7 @@
#include "wpi/nt/ntcore_c.h"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/SpanMatcher.hpp"
#include "wpi/util/Synchronization.hpp"
using ::testing::_;
using ::testing::AllOf;
@@ -38,7 +39,9 @@ namespace wpi::nt {
Field("pollStorage", &PubSubOptionsImpl::pollStorage, good.pollStorage),
Field("sendAll", &PubSubOptionsImpl::sendAll, good.sendAll),
Field("keepDuplicates", &PubSubOptionsImpl::keepDuplicates,
good.keepDuplicates));
good.keepDuplicates),
Field("disableSignal", &PubSubOptionsImpl::disableSignal,
good.disableSignal));
}
::testing::Matcher<const PubSubOptionsImpl&> IsDefaultPubSubOptions() {
@@ -545,6 +548,55 @@ TEST_F(LocalStorageTest, SetValueInvalidHandle) {
EXPECT_FALSE(storage.SetEntryValue(0, {}));
}
TEST_F(LocalStorageTest, DisableSignalSubscriberQueuesWithoutSignaling) {
PubSubOptionsImpl subOptions;
subOptions.disableSignal = true;
EXPECT_CALL(network,
ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}),
IsPubSubOptions(subOptions)));
auto sub =
storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableSignal = true});
EXPECT_CALL(
network,
ClientPublish(_, std::string_view{"foo"}, std::string_view{"double"},
wpi::util::json::object(), IsDefaultPubSubOptions()));
auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {});
auto val = Value::MakeDouble(1.0, 50);
EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val));
storage.SetEntryValue(pub, val);
bool timedOut = false;
EXPECT_FALSE(wpi::util::WaitForObject(sub, 0, &timedOut));
EXPECT_TRUE(timedOut);
auto values = storage.ReadQueue<double>(sub);
ASSERT_EQ(values.size(), 1u);
EXPECT_EQ(values[0].value, 1.0);
EXPECT_EQ(values[0].time, 50);
}
TEST_F(LocalStorageTest, DisableSignalMultiSubscriberDoesNotSignalHandle) {
PubSubOptionsImpl subOptions;
subOptions.disableSignal = true;
EXPECT_CALL(network, ClientSubscribe(_, _, IsPubSubOptions(subOptions)));
auto sub = storage.SubscribeMultiple({{""}}, {.disableSignal = true});
EXPECT_CALL(
network,
ClientPublish(_, std::string_view{"foo"}, std::string_view{"double"},
wpi::util::json::object(), IsDefaultPubSubOptions()));
auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {});
auto val = Value::MakeDouble(1.0, 50);
EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val));
storage.SetEntryValue(pub, val);
bool timedOut = false;
EXPECT_FALSE(wpi::util::WaitForObject(sub, 0, &timedOut));
EXPECT_TRUE(timedOut);
}
class LocalStorageDuplicatesTest : public LocalStorageTest {
public:
void SetupPubSub(bool keepPub, bool keepSub);

View File

@@ -28,6 +28,10 @@ bool PubSubOptionsMatcher::MatchAndExplain(
*listener << "keepDuplicates mismatch ";
match = false;
}
if (val.disableSignal != good.disableSignal) {
*listener << "disableSignal mismatch ";
match = false;
}
return match;
}

View File

@@ -102,7 +102,8 @@ void PrintTo(const PubSubOptionsImpl& options, std::ostream* os) {
*os << "PubSubOptions{periodicMs=" << options.periodicMs
<< ", pollStorage=" << options.pollStorage
<< ", sendAll=" << options.sendAll
<< ", keepDuplicates=" << options.keepDuplicates << '}';
<< ", keepDuplicates=" << options.keepDuplicates
<< ", disableSignal=" << options.disableSignal << '}';
}
} // namespace wpi::nt