Skip to content

Commit 6206700

Browse files
authored
weighted_target_lb: avoid possibility of rescheduling a timer before it fires (grpc#29203)
* weighted_target_lb: avoid possibility of rescheduling a timer before it fires * don't check shutdown_ or weight_ in timer callback * fix memory leak
1 parent 70fa551 commit 6206700

File tree

1 file changed

+70
-43
lines changed

1 file changed

+70
-43
lines changed

src/core/ext/filters/client_channel/lb_policy/weighted_target/weighted_target.cc

+70-43
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,23 @@ class WeightedTargetLb : public LoadBalancingPolicy {
158158
RefCountedPtr<WeightedChild> weighted_child_;
159159
};
160160

161+
class DelayedRemovalTimer
162+
: public InternallyRefCounted<DelayedRemovalTimer> {
163+
public:
164+
explicit DelayedRemovalTimer(RefCountedPtr<WeightedChild> weighted_child);
165+
166+
void Orphan() override;
167+
168+
private:
169+
static void OnTimer(void* arg, grpc_error_handle error);
170+
void OnTimerLocked(grpc_error_handle error);
171+
172+
RefCountedPtr<WeightedChild> weighted_child_;
173+
grpc_timer timer_;
174+
grpc_closure on_timer_;
175+
bool timer_pending_ = true;
176+
};
177+
161178
// Methods for dealing with the child policy.
162179
OrphanablePtr<LoadBalancingPolicy> CreateChildPolicyLocked(
163180
const grpc_channel_args* args);
@@ -166,9 +183,6 @@ class WeightedTargetLb : public LoadBalancingPolicy {
166183
grpc_connectivity_state state, const absl::Status& status,
167184
std::unique_ptr<SubchannelPicker> picker);
168185

169-
static void OnDelayedRemovalTimer(void* arg, grpc_error_handle error);
170-
void OnDelayedRemovalTimerLocked(grpc_error_handle error);
171-
172186
// The owning LB policy.
173187
RefCountedPtr<WeightedTargetLb> weighted_target_policy_;
174188

@@ -182,11 +196,7 @@ class WeightedTargetLb : public LoadBalancingPolicy {
182196
grpc_connectivity_state connectivity_state_ = GRPC_CHANNEL_CONNECTING;
183197
bool seen_failure_since_ready_ = false;
184198

185-
// States for delayed removal.
186-
grpc_timer delayed_removal_timer_;
187-
grpc_closure on_delayed_removal_timer_;
188-
bool delayed_removal_timer_callback_pending_ = false;
189-
bool shutdown_ = false;
199+
OrphanablePtr<DelayedRemovalTimer> delayed_removal_timer_;
190200
};
191201

192202
~WeightedTargetLb() override;
@@ -401,6 +411,53 @@ void WeightedTargetLb::UpdateStateLocked() {
401411
std::move(picker));
402412
}
403413

414+
//
415+
// WeightedTargetLb::WeightedChild::DelayedRemovalTimer
416+
//
417+
418+
WeightedTargetLb::WeightedChild::DelayedRemovalTimer::DelayedRemovalTimer(
419+
RefCountedPtr<WeightedTargetLb::WeightedChild> weighted_child)
420+
: weighted_child_(std::move(weighted_child)) {
421+
GRPC_CLOSURE_INIT(&on_timer_, OnTimer, this, nullptr);
422+
Ref().release();
423+
grpc_timer_init(&timer_, ExecCtx::Get()->Now() + kChildRetentionInterval,
424+
&on_timer_);
425+
}
426+
427+
void WeightedTargetLb::WeightedChild::DelayedRemovalTimer::Orphan() {
428+
if (timer_pending_) {
429+
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_weighted_target_trace)) {
430+
gpr_log(GPR_INFO,
431+
"[weighted_target_lb %p] WeightedChild %p %s: cancelling "
432+
"delayed removal timer",
433+
weighted_child_->weighted_target_policy_.get(),
434+
weighted_child_.get(), weighted_child_->name_.c_str());
435+
}
436+
timer_pending_ = false;
437+
grpc_timer_cancel(&timer_);
438+
}
439+
Unref();
440+
}
441+
442+
void WeightedTargetLb::WeightedChild::DelayedRemovalTimer::OnTimer(
443+
void* arg, grpc_error_handle error) {
444+
auto* self = static_cast<DelayedRemovalTimer*>(arg);
445+
(void)GRPC_ERROR_REF(error); // ref owned by lambda
446+
self->weighted_child_->weighted_target_policy_->work_serializer()->Run(
447+
[self, error]() { self->OnTimerLocked(error); }, DEBUG_LOCATION);
448+
}
449+
450+
void WeightedTargetLb::WeightedChild::DelayedRemovalTimer::OnTimerLocked(
451+
grpc_error_handle error) {
452+
if (error == GRPC_ERROR_NONE && timer_pending_) {
453+
timer_pending_ = false;
454+
weighted_child_->weighted_target_policy_->targets_.erase(
455+
weighted_child_->name_);
456+
}
457+
GRPC_ERROR_UNREF(error);
458+
Unref();
459+
}
460+
404461
//
405462
// WeightedTargetLb::WeightedChild
406463
//
@@ -413,8 +470,6 @@ WeightedTargetLb::WeightedChild::WeightedChild(
413470
gpr_log(GPR_INFO, "[weighted_target_lb %p] created WeightedChild %p for %s",
414471
weighted_target_policy_.get(), this, name_.c_str());
415472
}
416-
GRPC_CLOSURE_INIT(&on_delayed_removal_timer_, OnDelayedRemovalTimer, this,
417-
grpc_schedule_on_exec_ctx);
418473
}
419474

420475
WeightedTargetLb::WeightedChild::~WeightedChild() {
@@ -441,11 +496,7 @@ void WeightedTargetLb::WeightedChild::Orphan() {
441496
// Drop our ref to the child's picker, in case it's holding a ref to
442497
// the child.
443498
picker_wrapper_.reset();
444-
if (delayed_removal_timer_callback_pending_) {
445-
delayed_removal_timer_callback_pending_ = false;
446-
grpc_timer_cancel(&delayed_removal_timer_);
447-
}
448-
shutdown_ = true;
499+
delayed_removal_timer_.reset();
449500
Unref();
450501
}
451502

@@ -484,14 +535,13 @@ void WeightedTargetLb::WeightedChild::UpdateLocked(
484535
// Update child weight.
485536
weight_ = config.weight;
486537
// Reactivate if needed.
487-
if (delayed_removal_timer_callback_pending_) {
538+
if (delayed_removal_timer_ != nullptr) {
488539
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_weighted_target_trace)) {
489540
gpr_log(GPR_INFO,
490541
"[weighted_target_lb %p] WeightedChild %p %s: reactivating",
491542
weighted_target_policy_.get(), this, name_.c_str());
492543
}
493-
delayed_removal_timer_callback_pending_ = false;
494-
grpc_timer_cancel(&delayed_removal_timer_);
544+
delayed_removal_timer_.reset();
495545
}
496546
// Create child policy if needed.
497547
if (child_policy_ == nullptr) {
@@ -561,31 +611,8 @@ void WeightedTargetLb::WeightedChild::DeactivateLocked() {
561611
// Set the child weight to 0 so that future picker won't contain this child.
562612
weight_ = 0;
563613
// Start a timer to delete the child.
564-
Ref(DEBUG_LOCATION, "WeightedChild+timer").release();
565-
delayed_removal_timer_callback_pending_ = true;
566-
grpc_timer_init(&delayed_removal_timer_,
567-
ExecCtx::Get()->Now() + kChildRetentionInterval,
568-
&on_delayed_removal_timer_);
569-
}
570-
571-
void WeightedTargetLb::WeightedChild::OnDelayedRemovalTimer(
572-
void* arg, grpc_error_handle error) {
573-
WeightedChild* self = static_cast<WeightedChild*>(arg);
574-
(void)GRPC_ERROR_REF(error); // ref owned by lambda
575-
self->weighted_target_policy_->work_serializer()->Run(
576-
[self, error]() { self->OnDelayedRemovalTimerLocked(error); },
577-
DEBUG_LOCATION);
578-
}
579-
580-
void WeightedTargetLb::WeightedChild::OnDelayedRemovalTimerLocked(
581-
grpc_error_handle error) {
582-
if (error == GRPC_ERROR_NONE && delayed_removal_timer_callback_pending_ &&
583-
!shutdown_ && weight_ == 0) {
584-
delayed_removal_timer_callback_pending_ = false;
585-
weighted_target_policy_->targets_.erase(name_);
586-
}
587-
Unref(DEBUG_LOCATION, "WeightedChild+timer");
588-
GRPC_ERROR_UNREF(error);
614+
delayed_removal_timer_ = MakeOrphanable<DelayedRemovalTimer>(
615+
Ref(DEBUG_LOCATION, "DelayedRemovalTimer"));
589616
}
590617

591618
//

0 commit comments

Comments
 (0)