Skip to content

Commit df918a1

Browse files
committed
fix: resolve condition promise on Close to break leak cycles
1 parent 0208900 commit df918a1

4 files changed

Lines changed: 18 additions & 7 deletions

File tree

libs/server-sdk/src/data_interfaces/source/ifdv2_condition.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ namespace launchdarkly::server_side::data_interfaces {
3333
class IFDv2Condition {
3434
public:
3535
enum class Type {
36+
/** Stop the active synchronizer and start the next-preferred one. */
3637
kFallback,
38+
/** Return to the most-preferred synchronizer. */
3739
kRecovery,
40+
/** The condition was Close()d before firing; orchestrator ignores. */
41+
kCancelled,
3842
};
3943

4044
/**

libs/server-sdk/src/data_systems/fdv2/conditions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ void TimedCondition::Close() {
3535
state_->timer_cancel->Cancel();
3636
state_->timer_cancel.reset();
3737
}
38+
state_->promise.Resolve(IFDv2Condition::Type::kCancelled);
3839
}
3940

4041
void TimedCondition::ArmTimer() {

libs/server-sdk/src/data_systems/fdv2/fdv2_data_system.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ void FDv2DataSystem::RunSynchronizerNext() {
260260
void FDv2DataSystem::OnConditionFired(
261261
data_interfaces::IFDv2Condition::Type type) {
262262
using Type = data_interfaces::IFDv2Condition::Type;
263+
if (type == Type::kCancelled) {
264+
return;
265+
}
263266
{
264267
std::lock_guard<std::mutex> lock(mutex_);
265268
if (closed_) {

libs/server-sdk/tests/conditions_test.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ TEST(FallbackConditionTest, ChangeSetCancelsActiveTimer) {
9090
EXPECT_FALSE(future.IsFinished());
9191
}
9292

93-
TEST(FallbackConditionTest, CloseCancelsActiveTimer) {
93+
TEST(FallbackConditionTest, CloseCancelsActiveTimerAndResolvesWithCancelled) {
9494
RunningIoContext ioc;
9595
FallbackCondition condition(ioc.GetExecutor(), /*timeout=*/100ms);
9696
auto future = condition.Execute();
@@ -103,8 +103,9 @@ TEST(FallbackConditionTest, CloseCancelsActiveTimer) {
103103
}});
104104
condition.Close();
105105

106-
std::this_thread::sleep_for(200ms);
107-
EXPECT_FALSE(future.IsFinished());
106+
auto result = future.WaitForResult(200ms);
107+
ASSERT_TRUE(result.has_value());
108+
EXPECT_EQ(IFDv2Condition::Type::kCancelled, *result);
108109
}
109110

110111
// ============================================================================
@@ -148,15 +149,16 @@ TEST(RecoveryConditionTest, InformDoesNotAffectTimer) {
148149
EXPECT_EQ(IFDv2Condition::Type::kRecovery, *result);
149150
}
150151

151-
TEST(RecoveryConditionTest, CloseCancelsActiveTimer) {
152+
TEST(RecoveryConditionTest, CloseCancelsActiveTimerAndResolvesWithCancelled) {
152153
RunningIoContext ioc;
153154
RecoveryCondition condition(ioc.GetExecutor(), /*timeout=*/100ms);
154155
auto future = condition.Execute();
155156

156157
condition.Close();
157158

158-
std::this_thread::sleep_for(200ms);
159-
EXPECT_FALSE(future.IsFinished());
159+
auto result = future.WaitForResult(200ms);
160+
ASSERT_TRUE(result.has_value());
161+
EXPECT_EQ(IFDv2Condition::Type::kCancelled, *result);
160162
}
161163

162164
// ============================================================================
@@ -226,5 +228,6 @@ TEST(ConditionsTest, CloseForwardsToAllUnderlyingConditions) {
226228
conditions.Close();
227229

228230
auto result = conditions.GetFuture().WaitForResult(200ms);
229-
EXPECT_FALSE(result.has_value());
231+
ASSERT_TRUE(result.has_value());
232+
EXPECT_EQ(IFDv2Condition::Type::kCancelled, *result);
230233
}

0 commit comments

Comments
 (0)