Skip to content

Commit 7a481f8

Browse files
authored
Fix possible race confition when adding, then removing remote config services (#882)
Signed-off-by: Bob Weinand <[email protected]>
1 parent 958830a commit 7a481f8

File tree

1 file changed

+55
-30
lines changed

1 file changed

+55
-30
lines changed

remote-config/src/fetch/multitarget.rs

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -168,42 +168,67 @@ where
168168
'service_handling: {
169169
'drop_service: {
170170
if let Some(known_service) = services.get_mut(target) {
171-
known_service.refcount = if known_service.refcount == 1 {
172-
known_service.runtimes.remove(runtime_id);
173-
let mut status = known_service.status.lock().unwrap();
174-
*status = match *status {
175-
KnownTargetStatus::Pending => KnownTargetStatus::Alive, // not really
176-
KnownTargetStatus::Alive => KnownTargetStatus::RemoveAt(
177-
Instant::now() + Duration::from_secs(3666),
178-
),
179-
KnownTargetStatus::RemoveAt(_) | KnownTargetStatus::Removing(_) => {
180-
unreachable!()
171+
known_service.refcount = match known_service.refcount {
172+
0 => {
173+
// Handle the possible race condition where the service gets added AND
174+
// removed while in Removing state.
175+
let status = known_service.status.lock().unwrap();
176+
match *status {
177+
KnownTargetStatus::Removing(ref future) => {
178+
let future = future.clone();
179+
let runtime_id = runtime_id.to_string();
180+
let this = self.clone();
181+
let target = target.clone();
182+
tokio::spawn(async move {
183+
future.await;
184+
this.remove_target(runtime_id.as_str(), &target);
185+
});
186+
return;
187+
}
188+
_ => {
189+
// It's already in process of being removed
190+
return;
191+
}
181192
}
182-
};
183-
// We've marked it Alive so that the Pending check in start_fetcher() will
184-
// fail
185-
if matches!(*status, KnownTargetStatus::Alive) {
186-
break 'drop_service;
187193
}
188-
0
189-
} else {
190-
if *known_service.fetcher.runtime_id.lock().unwrap() == runtime_id {
191-
'changed_rt_id: {
192-
for (id, runtime) in self.runtimes.lock().unwrap().iter() {
193-
if runtime.targets.len() == 1
194-
&& runtime.targets.contains_key(target)
195-
{
196-
*known_service.fetcher.runtime_id.lock().unwrap() =
197-
id.to_string();
198-
break 'changed_rt_id;
194+
1 => {
195+
known_service.runtimes.remove(runtime_id);
196+
let mut status = known_service.status.lock().unwrap();
197+
*status = match *status {
198+
KnownTargetStatus::Pending => KnownTargetStatus::Alive, /* not really */
199+
KnownTargetStatus::Alive => KnownTargetStatus::RemoveAt(
200+
Instant::now() + Duration::from_secs(3666),
201+
),
202+
KnownTargetStatus::RemoveAt(_) | KnownTargetStatus::Removing(_) => {
203+
unreachable!()
204+
}
205+
};
206+
// We've marked it Alive so that the Pending check in start_fetcher()
207+
// will fail
208+
if matches!(*status, KnownTargetStatus::Alive) {
209+
break 'drop_service;
210+
}
211+
0
212+
}
213+
_ => {
214+
if *known_service.fetcher.runtime_id.lock().unwrap() == runtime_id {
215+
'changed_rt_id: {
216+
for (id, runtime) in self.runtimes.lock().unwrap().iter() {
217+
if runtime.targets.len() == 1
218+
&& runtime.targets.contains_key(target)
219+
{
220+
*known_service.fetcher.runtime_id.lock().unwrap() =
221+
id.to_string();
222+
break 'changed_rt_id;
223+
}
199224
}
225+
known_service.synthetic_id = true;
226+
*known_service.fetcher.runtime_id.lock().unwrap() =
227+
Self::generate_synthetic_id();
200228
}
201-
known_service.synthetic_id = true;
202-
*known_service.fetcher.runtime_id.lock().unwrap() =
203-
Self::generate_synthetic_id();
204229
}
230+
known_service.refcount - 1
205231
}
206-
known_service.refcount - 1
207232
};
208233
break 'service_handling;
209234
}

0 commit comments

Comments
 (0)