From d03954772506e6855407a1bea662d352afd987ad Mon Sep 17 00:00:00 2001 From: afrind Date: Thu, 28 May 2026 10:08:03 -0400 Subject: [PATCH] relay_thread config and allow > 1 thread in config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a use_relay_thread boolean config option (default: true) that controls whether relay state is isolated on a dedicated executor thread. Disabling it is intended for baseline performance comparison only. Also removes the hard error that rejected threads > 1, replacing it with a targeted check: threads > 1 requires use_relay_thread=true. This unlocks the config validation only — threads > 1 with use_relay_thread=true will race on shared relay state until the following commit wires up the dedicated relay executor. --- src/config/Config.h | 1 + src/config/ConfigResolver.cpp | 8 ++++++-- src/config/loader/ParsedConfig.h | 5 +++++ test/config/ConfigResolverTest.cpp | 21 +++++++++++++++++++-- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/config/Config.h b/src/config/Config.h index 1f45b18..8efeccf 100644 --- a/src/config/Config.h +++ b/src/config/Config.h @@ -196,6 +196,7 @@ struct Config { std::optional admin; std::string relayID; // always set: from config or randomly generated uint32_t threads{1}; + bool useRelayThread{true}; bool mvfstBpfSteering{true}; }; diff --git a/src/config/ConfigResolver.cpp b/src/config/ConfigResolver.cpp index 33d7a8e..c97e288 100644 --- a/src/config/ConfigResolver.cpp +++ b/src/config/ConfigResolver.cpp @@ -822,8 +822,11 @@ folly::Expected resolveConfig(const ParsedConfig& c const uint32_t threads = config.threads.value().value_or(1); if (threads == 0) { errors.push_back("threads must be >= 1"); - } else if (threads > 1) { - errors.push_back("threads > 1 is not yet supported"); + } + + const bool useRelayThread = config.use_relay_thread.value().value_or(true); + if (threads > 1 && !useRelayThread) { + errors.push_back("use_relay_thread must be true when threads > 1"); } const bool mvfstBpfSteering = config.mvfst_bpf_steering.value().value_or(true); @@ -877,6 +880,7 @@ folly::Expected resolveConfig(const ParsedConfig& c .admin = std::move(adminConfig), .relayID = std::move(relayID), .threads = threads, + .useRelayThread = useRelayThread, .mvfstBpfSteering = mvfstBpfSteering, }, .warnings = std::move(warnings), diff --git a/src/config/loader/ParsedConfig.h b/src/config/loader/ParsedConfig.h index 178d6a2..b075ff3 100644 --- a/src/config/loader/ParsedConfig.h +++ b/src/config/loader/ParsedConfig.h @@ -387,6 +387,11 @@ struct ParsedConfig { std::optional> listener_defaults; rfl::Description<"Number of IO worker threads (default: 1)", std::optional> threads; + rfl::Description< + "Dedicate one relay thread per service for relay state isolation (default: true). " + "Disable for baseline performance comparison.", + std::optional> + use_relay_thread; rfl::Description< "Attach a classic BPF reuseport filter to steer QUIC packets to the correct mvfst worker " "based on the connection ID's workerId field (Linux only, mvfst stack only, default: true). " diff --git a/test/config/ConfigResolverTest.cpp b/test/config/ConfigResolverTest.cpp index ac08fc7..37e8d82 100644 --- a/test/config/ConfigResolverTest.cpp +++ b/test/config/ConfigResolverTest.cpp @@ -920,12 +920,29 @@ TEST(ResolveConfig, ThreadsZeroRejected) { EXPECT_THAT(result.error(), HasSubstr("threads must be >= 1")); } -TEST(ResolveConfig, ThreadsGreaterThanOneRejected) { +TEST(ResolveConfig, ThreadsGreaterThanOneAccepted) { auto cfg = makeMinimalInsecureConfig(); cfg.threads = std::optional{2}; auto result = resolveConfig(cfg); + ASSERT_TRUE(result.hasValue()); + EXPECT_EQ(result.value().config.threads, 2u); +} + +TEST(ResolveConfig, UseRelayThreadFalseWithOneThreadAccepted) { + auto cfg = makeMinimalInsecureConfig(); + cfg.use_relay_thread = std::optional{false}; + auto result = resolveConfig(cfg); + ASSERT_TRUE(result.hasValue()); + EXPECT_FALSE(result.value().config.useRelayThread); +} + +TEST(ResolveConfig, UseRelayThreadFalseWithMultipleThreadsRejected) { + auto cfg = makeMinimalInsecureConfig(); + cfg.threads = std::optional{2}; + cfg.use_relay_thread = std::optional{false}; + auto result = resolveConfig(cfg); ASSERT_TRUE(result.hasError()); - EXPECT_THAT(result.error(), HasSubstr("threads > 1 is not yet supported")); + EXPECT_THAT(result.error(), HasSubstr("use_relay_thread must be true when threads > 1")); } // --- multiple listeners tests ---