Skip to content

Commit 5af63c3

Browse files
authored
RUST-1779 Fix a memory leak in cleanup tracking (#979) (#984)
1 parent 0babc83 commit 5af63c3

File tree

2 files changed

+40
-23
lines changed

2 files changed

+40
-23
lines changed

src/client.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -504,12 +504,9 @@ impl Client {
504504
// await points in between.
505505
let id = id_rx.await.unwrap();
506506
// If the cleanup channel is closed, that task was dropped.
507-
let cleanup = if let Ok(f) = cleanup_rx.await {
508-
f
509-
} else {
510-
return;
511-
};
512-
cleanup.await;
507+
if let Ok(cleanup) = cleanup_rx.await {
508+
cleanup.await;
509+
}
513510
if let Some(client) = weak.upgrade() {
514511
client
515512
.inner

src/id_set.rs

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,62 @@
1-
use std::collections::HashMap;
2-
3-
/// A set that provides removal tokens when an item is added.
1+
/// A container that provides removal tokens when an item is added.
42
#[derive(Debug, Clone)]
53
pub(crate) struct IdSet<T> {
6-
values: HashMap<u32, T>,
7-
// Incrementing a counter is not the best source of tokens - it can
8-
// cause poor hash behavior - but efficiency is not an immediate concern.
9-
next_id: u32,
4+
values: Vec<Entry<T>>,
5+
free: Vec<usize>,
6+
}
7+
8+
#[derive(Debug, Clone)]
9+
struct Entry<T> {
10+
generation: u32,
11+
value: Option<T>,
1012
}
1113

1214
#[derive(Debug, Clone, PartialEq, Eq)]
13-
pub(crate) struct Id(u32);
15+
pub(crate) struct Id {
16+
index: usize,
17+
generation: u32,
18+
}
1419

1520
impl<T> IdSet<T> {
1621
pub(crate) fn new() -> Self {
1722
Self {
18-
values: HashMap::new(),
19-
next_id: 0,
23+
values: vec![],
24+
free: vec![],
2025
}
2126
}
2227

2328
pub(crate) fn insert(&mut self, value: T) -> Id {
24-
let id = self.next_id;
25-
self.next_id += 1;
26-
self.values.insert(id, value);
27-
Id(id)
29+
let value = Some(value);
30+
if let Some(index) = self.free.pop() {
31+
let generation = self.values[index].generation + 1;
32+
self.values[index] = Entry { generation, value };
33+
Id { index, generation }
34+
} else {
35+
let generation = 0;
36+
self.values.push(Entry { generation, value });
37+
Id {
38+
index: self.values.len() - 1,
39+
generation,
40+
}
41+
}
2842
}
2943

3044
pub(crate) fn remove(&mut self, id: &Id) {
31-
self.values.remove(&id.0);
45+
if let Some(entry) = self.values.get_mut(id.index) {
46+
if entry.generation == id.generation {
47+
entry.value = None;
48+
self.free.push(id.index);
49+
}
50+
}
3251
}
3352

3453
#[cfg(all(test, mongodb_internal_tracking_arc))]
3554
pub(crate) fn values(&self) -> impl Iterator<Item = &T> {
36-
self.values.values()
55+
self.values.iter().filter_map(|e| e.value.as_ref())
3756
}
3857

3958
pub(crate) fn extract(&mut self) -> Vec<T> {
40-
self.values.drain().map(|(_, v)| v).collect()
59+
self.free.clear();
60+
self.values.drain(..).filter_map(|e| e.value).collect()
4161
}
4262
}

0 commit comments

Comments
 (0)