Coverage Report

Created: 2025-10-13 17:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-util/src/evicting_map.rs
Line
Count
Source
1
// Copyright 2024 The NativeLink Authors. All rights reserved.
2
//
3
// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    See LICENSE file for details
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
use core::borrow::Borrow;
16
use core::cmp::Eq;
17
use core::fmt::Debug;
18
use core::future::Future;
19
use core::hash::Hash;
20
use core::ops::RangeBounds;
21
use std::collections::BTreeSet;
22
use std::sync::Arc;
23
24
use lru::LruCache;
25
use nativelink_config::stores::EvictionPolicy;
26
use nativelink_metric::MetricsComponent;
27
use parking_lot::Mutex;
28
use serde::{Deserialize, Serialize};
29
use tonic::async_trait;
30
use tracing::{debug, info};
31
32
use crate::instant_wrapper::InstantWrapper;
33
use crate::metrics_utils::{Counter, CounterWithTime};
34
35
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
36
pub struct SerializedLRU<K> {
37
    pub data: Vec<(K, i32)>,
38
    pub anchor_time: u64,
39
}
40
41
#[derive(Debug)]
42
struct EvictionItem<T: LenEntry + Debug> {
43
    seconds_since_anchor: i32,
44
    data: T,
45
}
46
47
pub trait LenEntry: 'static {
48
    /// Length of referenced data.
49
    fn len(&self) -> u64;
50
51
    /// Returns `true` if `self` has zero length.
52
    fn is_empty(&self) -> bool;
53
54
    /// This will be called when object is removed from map.
55
    /// Note: There may still be a reference to it held somewhere else, which
56
    /// is why it can't be mutable. This is a good place to mark the item
57
    /// to be deleted and then in the Drop call actually do the deleting.
58
    /// This will ensure nowhere else in the program still holds a reference
59
    /// to this object.
60
    /// You should not rely only on the Drop trait. Doing so might result in the
61
    /// program safely shutting down and calling the Drop method on each object,
62
    /// which if you are deleting items you may not want to do.
63
    /// It is undefined behavior to have `unref()` called more than once.
64
    /// During the execution of `unref()` no items can be added or removed to/from
65
    /// the `EvictionMap` globally (including inside `unref()`).
66
    #[inline]
67
46
    fn unref(&self) -> impl Future<Output = ()> + Send {
68
46
        core::future::ready(())
69
46
    }
70
}
71
72
impl<T: LenEntry + Send + Sync> LenEntry for Arc<T> {
73
    #[inline]
74
281
    fn len(&self) -> u64 {
75
281
        T::len(self.as_ref())
76
281
    }
77
78
    #[inline]
79
0
    fn is_empty(&self) -> bool {
80
0
        T::is_empty(self.as_ref())
81
0
    }
82
83
    #[inline]
84
28
    async fn unref(&self) {
85
28
        self.as_ref().unref().await;
86
28
    }
87
}
88
89
// Callback to be called when the EvictingMap removes an item
90
// either via eviction or direct deletion. This will be called with
91
// whatever key type the EvictingMap uses.
92
#[async_trait]
93
pub trait RemoveStateCallback<Q>: Debug + Send + Sync {
94
    async fn callback(&self, key: &Q);
95
}
96
97
#[derive(Debug, MetricsComponent)]
98
struct State<
99
    K: Ord + Hash + Eq + Clone + Debug + Send + Borrow<Q>,
100
    Q: Ord + Hash + Eq + Debug,
101
    T: LenEntry + Debug + Send,
102
> {
103
    lru: LruCache<K, EvictionItem<T>>,
104
    btree: Option<BTreeSet<K>>,
105
    #[metric(help = "Total size of all items in the store")]
106
    sum_store_size: u64,
107
108
    #[metric(help = "Number of bytes evicted from the store")]
109
    evicted_bytes: Counter,
110
    #[metric(help = "Number of items evicted from the store")]
111
    evicted_items: CounterWithTime,
112
    #[metric(help = "Number of bytes replaced in the store")]
113
    replaced_bytes: Counter,
114
    #[metric(help = "Number of items replaced in the store")]
115
    replaced_items: CounterWithTime,
116
    #[metric(help = "Number of bytes inserted into the store since it was created")]
117
    lifetime_inserted_bytes: Counter,
118
119
    remove_callbacks: Arc<Mutex<Vec<Box<dyn RemoveStateCallback<Q>>>>>,
120
}
121
122
impl<
123
    K: Ord + Hash + Eq + Clone + Debug + Send + Sync + Borrow<Q>,
124
    Q: Ord + Hash + Eq + Debug + Sync,
125
    T: LenEntry + Debug + Sync + Send,
126
> State<K, Q, T>
127
{
128
    /// Removes an item from the cache and returns the data for deferred cleanup.
129
    /// The caller is responsible for calling `unref()` on the returned data outside of the lock.
130
    #[must_use]
131
73
    async fn remove(&mut self, key: &Q, eviction_item: &EvictionItem<T>, replaced: bool) -> T
132
73
    where
133
73
        T: Clone,
134
73
    {
135
73
        if let Some(
btree0
) = &mut self.btree {
  Branch (135:16): [True: 0, False: 0]
  Branch (135:16): [True: 0, False: 20]
  Branch (135:16): [True: 0, False: 0]
  Branch (135:16): [True: 0, False: 0]
  Branch (135:16): [Folded - Ignored]
  Branch (135:16): [True: 0, False: 6]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 14]
  Branch (135:16): [True: 0, False: 0]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 3]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 1]
  Branch (135:16): [True: 0, False: 5]
  Branch (135:16): [True: 0, False: 0]
  Branch (135:16): [True: 0, False: 0]
  Branch (135:16): [True: 0, False: 0]
  Branch (135:16): [Folded - Ignored]
  Branch (135:16): [True: 0, False: 16]
  Branch (135:16): [True: 0, False: 1]
136
0
            btree.remove(key.borrow());
137
73
        }
138
73
        self.sum_store_size -= eviction_item.data.len();
139
73
        if replaced {
  Branch (139:12): [True: 0, False: 0]
  Branch (139:12): [True: 18, False: 2]
  Branch (139:12): [True: 0, False: 0]
  Branch (139:12): [True: 0, False: 0]
  Branch (139:12): [Folded - Ignored]
  Branch (139:12): [True: 0, False: 6]
  Branch (139:12): [True: 0, False: 1]
  Branch (139:12): [True: 1, False: 0]
  Branch (139:12): [True: 0, False: 14]
  Branch (139:12): [True: 0, False: 0]
  Branch (139:12): [True: 0, False: 1]
  Branch (139:12): [True: 0, False: 3]
  Branch (139:12): [True: 0, False: 1]
  Branch (139:12): [True: 0, False: 1]
  Branch (139:12): [True: 1, False: 0]
  Branch (139:12): [True: 1, False: 0]
  Branch (139:12): [True: 0, False: 1]
  Branch (139:12): [True: 3, False: 2]
  Branch (139:12): [True: 0, False: 0]
  Branch (139:12): [True: 0, False: 0]
  Branch (139:12): [True: 0, False: 0]
  Branch (139:12): [Folded - Ignored]
  Branch (139:12): [True: 16, False: 0]
  Branch (139:12): [True: 0, False: 1]
140
40
            self.replaced_items.inc();
141
40
            self.replaced_bytes.add(eviction_item.data.len());
142
40
        } else {
143
33
            self.evicted_items.inc();
144
33
            self.evicted_bytes.add(eviction_item.data.len());
145
33
        }
146
147
73
        let locked_callbacks = self.remove_callbacks.lock_arc();
148
73
        for 
callback2
in locked_callbacks.iter() {
149
2
            callback.callback(key).await;
150
        }
151
152
        // Return the data for deferred unref outside of lock
153
73
        eviction_item.data.clone()
154
73
    }
155
156
    /// Inserts a new item into the cache. If the key already exists, the old item is returned
157
    /// for deferred cleanup.
158
    #[must_use]
159
5.94k
    async fn put(&mut self, key: &K, eviction_item: EvictionItem<T>) -> Option<T>
160
5.94k
    where
161
5.94k
        K: Clone,
162
5.94k
        T: Clone,
163
5.94k
    {
164
        // If we are maintaining a btree index, we need to update it.
165
5.94k
        if let Some(
btree0
) = &mut self.btree {
  Branch (165:16): [True: 0, False: 0]
  Branch (165:16): [True: 0, False: 5.75k]
  Branch (165:16): [True: 0, False: 0]
  Branch (165:16): [True: 0, False: 0]
  Branch (165:16): [Folded - Ignored]
  Branch (165:16): [True: 0, False: 2]
  Branch (165:16): [True: 0, False: 27]
  Branch (165:16): [True: 0, False: 3]
  Branch (165:16): [True: 0, False: 6]
  Branch (165:16): [True: 0, False: 2]
  Branch (165:16): [True: 0, False: 2]
  Branch (165:16): [True: 0, False: 2]
  Branch (165:16): [True: 0, False: 2]
  Branch (165:16): [True: 0, False: 1]
  Branch (165:16): [True: 0, False: 16]
  Branch (165:16): [True: 0, False: 0]
  Branch (165:16): [True: 0, False: 2]
  Branch (165:16): [Folded - Ignored]
  Branch (165:16): [True: 0, False: 97]
  Branch (165:16): [True: 0, False: 27]
166
0
            btree.insert(key.clone());
167
5.94k
        }
168
5.94k
        if let Some(
old_item40
) = self.lru.put(key.clone(), eviction_item) {
  Branch (168:16): [True: 0, False: 0]
  Branch (168:16): [True: 18, False: 5.73k]
  Branch (168:16): [True: 0, False: 0]
  Branch (168:16): [True: 0, False: 0]
  Branch (168:16): [Folded - Ignored]
  Branch (168:16): [True: 1, False: 1]
  Branch (168:16): [True: 0, False: 27]
  Branch (168:16): [True: 0, False: 3]
  Branch (168:16): [True: 0, False: 6]
  Branch (168:16): [True: 0, False: 2]
  Branch (168:16): [True: 0, False: 2]
  Branch (168:16): [True: 1, False: 1]
  Branch (168:16): [True: 1, False: 1]
  Branch (168:16): [True: 0, False: 1]
  Branch (168:16): [True: 3, False: 13]
  Branch (168:16): [True: 0, False: 0]
  Branch (168:16): [True: 0, False: 2]
  Branch (168:16): [Folded - Ignored]
  Branch (168:16): [True: 16, False: 81]
  Branch (168:16): [True: 0, False: 27]
169
40
            let old_data = self.remove(key.borrow(), &old_item, true).await;
170
40
            return Some(old_data);
171
5.90k
        }
172
5.90k
        None
173
5.94k
    }
174
175
5
    fn add_remove_callback(&self, callback: Box<dyn RemoveStateCallback<Q>>) {
176
5
        self.remove_callbacks.lock_arc().push(callback);
177
5
    }
178
}
179
180
#[derive(Debug, MetricsComponent)]
181
pub struct EvictingMap<
182
    K: Ord + Hash + Eq + Clone + Debug + Send + Borrow<Q>,
183
    Q: Ord + Hash + Eq + Debug,
184
    T: LenEntry + Debug + Send,
185
    I: InstantWrapper,
186
> {
187
    #[metric]
188
    state: Arc<Mutex<State<K, Q, T>>>,
189
    anchor_time: I,
190
    #[metric(help = "Maximum size of the store in bytes")]
191
    max_bytes: u64,
192
    #[metric(help = "Number of bytes to evict when the store is full")]
193
    evict_bytes: u64,
194
    #[metric(help = "Maximum number of seconds to keep an item in the store")]
195
    max_seconds: i32,
196
    #[metric(help = "Maximum number of items to keep in the store")]
197
    max_count: u64,
198
}
199
200
impl<K, Q, T, I> EvictingMap<K, Q, T, I>
201
where
202
    K: Ord + Hash + Eq + Clone + Debug + Send + Sync + Borrow<Q>,
203
    Q: Ord + Hash + Eq + Debug + Sync,
204
    T: LenEntry + Debug + Clone + Send + Sync,
205
    I: InstantWrapper,
206
{
207
406
    pub fn new(config: &EvictionPolicy, anchor_time: I) -> Self {
208
406
        Self {
209
406
            // We use unbounded because if we use the bounded version we can't call the delete
210
406
            // function on the LenEntry properly.
211
406
            state: Arc::new(Mutex::new(State {
212
406
                lru: LruCache::unbounded(),
213
406
                btree: None,
214
406
                sum_store_size: 0,
215
406
                evicted_bytes: Counter::default(),
216
406
                evicted_items: CounterWithTime::default(),
217
406
                replaced_bytes: Counter::default(),
218
406
                replaced_items: CounterWithTime::default(),
219
406
                lifetime_inserted_bytes: Counter::default(),
220
406
                remove_callbacks: Arc::new(Mutex::new(vec![])),
221
406
            })),
222
406
            anchor_time,
223
406
            max_bytes: config.max_bytes as u64,
224
406
            evict_bytes: config.evict_bytes as u64,
225
406
            max_seconds: config.max_seconds as i32,
226
406
            max_count: config.max_count,
227
406
        }
228
406
    }
229
230
0
    pub async fn enable_filtering(&self) {
231
0
        let mut state = self.state.lock_arc();
232
0
        if state.btree.is_none() {
  Branch (232:12): [Folded - Ignored]
  Branch (232:12): [Folded - Ignored]
233
0
            Self::rebuild_btree_index(&mut state);
234
0
        }
235
0
    }
236
237
2
    fn rebuild_btree_index(state: &mut State<K, Q, T>) {
238
2
        state.btree = Some(state.lru.iter().map(|(k, _)| k).cloned().collect());
239
2
    }
240
241
    /// Run the `handler` function on each key-value pair that matches the `prefix_range`
242
    /// and return the number of items that were processed.
243
    /// The `handler` function should return `true` to continue processing the next item
244
    /// or `false` to stop processing.
245
11
    pub async fn range<F>(&self, prefix_range: impl RangeBounds<Q> + Send, mut handler: F) -> u64
246
11
    where
247
11
        F: FnMut(&K, &T) -> bool + Send,
248
11
        K: Ord,
249
11
    {
250
11
        let mut state = self.state.lock_arc();
251
11
        let btree = if let Some(
ref btree9
) = state.btree {
  Branch (251:28): [True: 6, False: 1]
  Branch (251:28): [Folded - Ignored]
  Branch (251:28): [True: 1, False: 0]
  Branch (251:28): [True: 2, False: 0]
  Branch (251:28): [True: 0, False: 1]
  Branch (251:28): [Folded - Ignored]
252
9
            btree
253
        } else {
254
2
            Self::rebuild_btree_index(&mut state);
255
2
            state.btree.as_ref().unwrap()
256
        };
257
11
        let mut continue_count = 0;
258
22
        for key in 
btree11
.
range11
(
prefix_range11
) {
259
22
            let value = &state.lru.peek(key.borrow()).unwrap().data;
260
22
            let should_continue = handler(key, value);
261
22
            if !should_continue {
  Branch (261:16): [True: 0, False: 13]
  Branch (261:16): [Folded - Ignored]
  Branch (261:16): [True: 0, False: 1]
  Branch (261:16): [True: 0, False: 5]
  Branch (261:16): [True: 0, False: 3]
  Branch (261:16): [Folded - Ignored]
262
0
                break;
263
22
            }
264
22
            continue_count += 1;
265
        }
266
11
        continue_count
267
11
    }
268
269
    /// Returns the number of key-value pairs that are currently in the the cache.
270
    /// Function is not for production code paths.
271
30
    pub async fn len_for_test(&self) -> usize {
272
30
        self.state.lock_arc().lru.len()
273
30
    }
274
275
10.6k
    fn should_evict(
276
10.6k
        &self,
277
10.6k
        lru_len: usize,
278
10.6k
        peek_entry: &EvictionItem<T>,
279
10.6k
        sum_store_size: u64,
280
10.6k
        max_bytes: u64,
281
10.6k
    ) -> bool {
282
10.6k
        let is_over_size = max_bytes != 0 && 
sum_store_size >= max_bytes211
;
  Branch (282:28): [True: 0, False: 0]
  Branch (282:28): [True: 0, False: 0]
  Branch (282:28): [True: 103, False: 10.1k]
  Branch (282:28): [True: 0, False: 0]
  Branch (282:28): [Folded - Ignored]
  Branch (282:28): [True: 0, False: 6]
  Branch (282:28): [True: 0, False: 1]
  Branch (282:28): [True: 0, False: 3]
  Branch (282:28): [True: 30, False: 33]
  Branch (282:28): [True: 0, False: 3]
  Branch (282:28): [True: 0, False: 1]
  Branch (282:28): [True: 0, False: 2]
  Branch (282:28): [True: 3, False: 7]
  Branch (282:28): [True: 3, False: 0]
  Branch (282:28): [True: 0, False: 4]
  Branch (282:28): [True: 0, False: 2]
  Branch (282:28): [True: 0, False: 3]
  Branch (282:28): [True: 0, False: 2]
  Branch (282:28): [True: 1, False: 27]
  Branch (282:28): [True: 0, False: 0]
  Branch (282:28): [True: 0, False: 4]
  Branch (282:28): [True: 0, False: 3]
  Branch (282:28): [Folded - Ignored]
  Branch (282:28): [True: 71, False: 138]
  Branch (282:28): [True: 0, False: 85]
283
284
10.6k
        let evict_older_than_seconds =
285
10.6k
            (self.anchor_time.elapsed().as_secs() as i32) - self.max_seconds;
286
10.6k
        let old_item_exists =
287
10.6k
            self.max_seconds != 0 && 
peek_entry.seconds_since_anchor < evict_older_than_seconds115
;
  Branch (287:13): [True: 0, False: 0]
  Branch (287:13): [True: 0, False: 0]
  Branch (287:13): [True: 0, False: 10.2k]
  Branch (287:13): [True: 0, False: 0]
  Branch (287:13): [Folded - Ignored]
  Branch (287:13): [True: 0, False: 6]
  Branch (287:13): [True: 0, False: 1]
  Branch (287:13): [True: 0, False: 3]
  Branch (287:13): [True: 30, False: 33]
  Branch (287:13): [True: 0, False: 3]
  Branch (287:13): [True: 0, False: 1]
  Branch (287:13): [True: 0, False: 2]
  Branch (287:13): [True: 0, False: 10]
  Branch (287:13): [True: 0, False: 3]
  Branch (287:13): [True: 0, False: 4]
  Branch (287:13): [True: 0, False: 2]
  Branch (287:13): [True: 0, False: 3]
  Branch (287:13): [True: 0, False: 2]
  Branch (287:13): [True: 0, False: 28]
  Branch (287:13): [True: 0, False: 0]
  Branch (287:13): [True: 0, False: 4]
  Branch (287:13): [True: 0, False: 3]
  Branch (287:13): [Folded - Ignored]
  Branch (287:13): [True: 0, False: 209]
  Branch (287:13): [True: 85, False: 0]
288
289
10.6k
        let is_over_count = self.max_count != 0 && 
(lru_len as u64) > self.max_count20
;
  Branch (289:29): [True: 0, False: 0]
  Branch (289:29): [True: 0, False: 0]
  Branch (289:29): [True: 0, False: 10.2k]
  Branch (289:29): [True: 0, False: 0]
  Branch (289:29): [Folded - Ignored]
  Branch (289:29): [True: 0, False: 6]
  Branch (289:29): [True: 0, False: 1]
  Branch (289:29): [True: 3, False: 0]
  Branch (289:29): [True: 8, False: 55]
  Branch (289:29): [True: 0, False: 3]
  Branch (289:29): [True: 0, False: 1]
  Branch (289:29): [True: 0, False: 2]
  Branch (289:29): [True: 0, False: 10]
  Branch (289:29): [True: 0, False: 3]
  Branch (289:29): [True: 4, False: 0]
  Branch (289:29): [True: 2, False: 0]
  Branch (289:29): [True: 3, False: 0]
  Branch (289:29): [True: 0, False: 2]
  Branch (289:29): [True: 0, False: 28]
  Branch (289:29): [True: 0, False: 0]
  Branch (289:29): [True: 0, False: 4]
  Branch (289:29): [True: 0, False: 3]
  Branch (289:29): [Folded - Ignored]
  Branch (289:29): [True: 0, False: 209]
  Branch (289:29): [True: 0, False: 85]
290
291
10.6k
        is_over_size || 
old_item_exists10.6k
||
is_over_count10.6k
  Branch (291:9): [True: 0, False: 0]
  Branch (291:25): [True: 0, False: 0]
  Branch (291:9): [True: 0, False: 0]
  Branch (291:25): [True: 0, False: 0]
  Branch (291:9): [True: 2, False: 10.2k]
  Branch (291:25): [True: 0, False: 10.2k]
  Branch (291:9): [True: 0, False: 0]
  Branch (291:25): [True: 0, False: 0]
  Branch (291:9): [Folded - Ignored]
  Branch (291:25): [Folded - Ignored]
  Branch (291:9): [True: 0, False: 6]
  Branch (291:25): [True: 0, False: 6]
  Branch (291:9): [True: 0, False: 1]
  Branch (291:25): [True: 0, False: 1]
  Branch (291:9): [True: 0, False: 3]
  Branch (291:25): [True: 0, False: 3]
  Branch (291:9): [True: 6, False: 57]
  Branch (291:25): [True: 9, False: 48]
  Branch (291:9): [True: 0, False: 3]
  Branch (291:25): [True: 0, False: 3]
  Branch (291:9): [True: 0, False: 1]
  Branch (291:25): [True: 0, False: 1]
  Branch (291:9): [True: 0, False: 2]
  Branch (291:25): [True: 0, False: 2]
  Branch (291:9): [True: 0, False: 10]
  Branch (291:25): [True: 0, False: 10]
  Branch (291:9): [True: 1, False: 2]
  Branch (291:25): [True: 0, False: 2]
  Branch (291:9): [True: 0, False: 4]
  Branch (291:25): [True: 0, False: 4]
  Branch (291:9): [True: 0, False: 2]
  Branch (291:25): [True: 0, False: 2]
  Branch (291:9): [True: 0, False: 3]
  Branch (291:25): [True: 0, False: 3]
  Branch (291:9): [True: 0, False: 2]
  Branch (291:25): [True: 0, False: 2]
  Branch (291:9): [True: 0, False: 28]
  Branch (291:25): [True: 0, False: 28]
  Branch (291:9): [True: 0, False: 0]
  Branch (291:25): [True: 0, False: 0]
  Branch (291:9): [True: 0, False: 4]
  Branch (291:25): [True: 0, False: 4]
  Branch (291:9): [True: 0, False: 3]
  Branch (291:25): [True: 0, False: 3]
  Branch (291:9): [Folded - Ignored]
  Branch (291:25): [Folded - Ignored]
  Branch (291:9): [True: 0, False: 209]
  Branch (291:25): [True: 0, False: 209]
  Branch (291:9): [True: 0, False: 85]
  Branch (291:25): [True: 1, False: 84]
292
10.6k
    }
293
294
    #[must_use]
295
5.95k
    async fn evict_items(&self, state: &mut State<K, Q, T>) -> Vec<T> {
296
5.95k
        let Some((_, mut peek_entry)) = state.lru.peek_lru() else {
  Branch (296:13): [True: 0, False: 0]
  Branch (296:13): [True: 5.75k, False: 0]
  Branch (296:13): [True: 0, False: 0]
  Branch (296:13): [True: 0, False: 0]
  Branch (296:13): [Folded - Ignored]
  Branch (296:13): [True: 6, False: 0]
  Branch (296:13): [True: 1, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [True: 31, False: 0]
  Branch (296:13): [True: 3, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [True: 1, False: 0]
  Branch (296:13): [True: 7, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [True: 18, False: 0]
  Branch (296:13): [True: 0, False: 0]
  Branch (296:13): [True: 0, False: 0]
  Branch (296:13): [True: 2, False: 0]
  Branch (296:13): [Folded - Ignored]
  Branch (296:13): [True: 97, False: 0]
  Branch (296:13): [True: 27, False: 0]
297
0
            return Vec::new();
298
        };
299
300
5.95k
        let max_bytes = if self.max_bytes != 0
  Branch (300:28): [True: 0, False: 0]
  Branch (300:28): [True: 8, False: 5.74k]
  Branch (300:28): [True: 0, False: 0]
  Branch (300:28): [True: 0, False: 0]
  Branch (300:28): [Folded - Ignored]
  Branch (300:28): [True: 0, False: 6]
  Branch (300:28): [True: 0, False: 1]
  Branch (300:28): [True: 0, False: 2]
  Branch (300:28): [True: 8, False: 23]
  Branch (300:28): [True: 0, False: 3]
  Branch (300:28): [True: 0, False: 2]
  Branch (300:28): [True: 0, False: 1]
  Branch (300:28): [True: 0, False: 7]
  Branch (300:28): [True: 2, False: 0]
  Branch (300:28): [True: 0, False: 2]
  Branch (300:28): [True: 0, False: 2]
  Branch (300:28): [True: 0, False: 2]
  Branch (300:28): [True: 0, False: 2]
  Branch (300:28): [True: 0, False: 18]
  Branch (300:28): [True: 0, False: 0]
  Branch (300:28): [True: 0, False: 0]
  Branch (300:28): [True: 0, False: 2]
  Branch (300:28): [Folded - Ignored]
  Branch (300:28): [True: 0, False: 97]
  Branch (300:28): [True: 0, False: 27]
301
18
            && self.evict_bytes != 0
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 8]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [Folded - Ignored]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 4, False: 4]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 2]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [Folded - Ignored]
  Branch (301:16): [True: 0, False: 0]
  Branch (301:16): [True: 0, False: 0]
302
4
            && self.should_evict(
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [Folded - Ignored]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 1, False: 3]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [Folded - Ignored]
  Branch (302:16): [True: 0, False: 0]
  Branch (302:16): [True: 0, False: 0]
303
4
                state.lru.len(),
304
4
                peek_entry,
305
4
                state.sum_store_size,
306
4
                self.max_bytes,
307
            ) {
308
1
            self.max_bytes.saturating_sub(self.evict_bytes)
309
        } else {
310
5.95k
            self.max_bytes
311
        };
312
313
5.95k
        let mut items_to_unref = Vec::new();
314
315
5.97k
        while self.should_evict(state.lru.len(), peek_entry, state.sum_store_size, max_bytes) {
  Branch (315:15): [True: 0, False: 0]
  Branch (315:15): [True: 2, False: 5.75k]
  Branch (315:15): [True: 0, False: 0]
  Branch (315:15): [True: 0, False: 0]
  Branch (315:15): [Folded - Ignored]
  Branch (315:15): [True: 0, False: 6]
  Branch (315:15): [True: 0, False: 1]
  Branch (315:15): [True: 0, False: 2]
  Branch (315:15): [True: 12, False: 28]
  Branch (315:15): [True: 0, False: 3]
  Branch (315:15): [True: 0, False: 2]
  Branch (315:15): [True: 0, False: 1]
  Branch (315:15): [True: 0, False: 7]
  Branch (315:15): [True: 1, False: 2]
  Branch (315:15): [True: 1, False: 2]
  Branch (315:15): [True: 0, False: 2]
  Branch (315:15): [True: 0, False: 2]
  Branch (315:15): [True: 0, False: 2]
  Branch (315:15): [True: 0, False: 18]
  Branch (315:15): [True: 0, False: 0]
  Branch (315:15): [True: 0, False: 0]
  Branch (315:15): [True: 0, False: 2]
  Branch (315:15): [Folded - Ignored]
  Branch (315:15): [True: 0, False: 97]
  Branch (315:15): [True: 1, False: 27]
316
17
            let (key, eviction_item) = state
317
17
                .lru
318
17
                .pop_lru()
319
17
                .expect("Tried to peek() then pop() but failed");
320
17
            debug!(?key, "Evicting",);
321
17
            let data = state.remove(key.borrow(), &eviction_item, false).await;
322
17
            items_to_unref.push(data);
323
324
17
            peek_entry = if let Some((_, 
entry13
)) = state.lru.peek_lru() {
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 1, False: 1]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [Folded - Ignored]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 9, False: 3]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 1, False: 0]
  Branch (324:33): [True: 1, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [Folded - Ignored]
  Branch (324:33): [True: 0, False: 0]
  Branch (324:33): [True: 1, False: 0]
325
13
                entry
326
            } else {
327
4
                break;
328
            };
329
        }
330
331
5.95k
        items_to_unref
332
5.95k
    }
333
334
    /// Return the size of a `key`, if not found `None` is returned.
335
25
    pub async fn size_for_key(&self, key: &Q) -> Option<u64>
336
25
    where
337
25
        Q: Sync,
338
25
    {
339
25
        let mut results = [None];
340
25
        self.sizes_for_keys([key], &mut results[..], false).await;
341
25
        results[0]
342
25
    }
343
344
    /// Return the sizes of a collection of `keys`. Expects `results` collection
345
    /// to be provided for storing the resulting key sizes. Each index value in
346
    /// `keys` maps directly to the size value for the key in `results`.
347
    /// If no key is found in the internal map, `None` is filled in its place.
348
    /// If `peek` is set to `true`, the items are not promoted to the front of the
349
    /// LRU cache. Note: peek may still evict, but won't promote.
350
298
    pub async fn sizes_for_keys<It, R>(&self, keys: It, results: &mut [Option<u64>], peek: bool)
351
298
    where
352
298
        It: IntoIterator<Item = R> + Send,
353
298
        // Note: It's not enough to have the inserts themselves be Send. The
354
298
        // returned iterator should be Send as well.
355
298
        <It as IntoIterator>::IntoIter: Send,
356
298
        // This may look strange, but what we are doing is saying:
357
298
        // * `K` must be able to borrow `Q`
358
298
        // * `R` (the input stream item type) must also be able to borrow `Q`
359
298
        // Note: That K and R do not need to be the same type, they just both need
360
298
        // to be able to borrow a `Q`.
361
298
        R: Borrow<Q> + Send,
362
298
    {
363
298
        let mut state = self.state.lock_arc();
364
365
298
        let lru_len = state.lru.len();
366
333
        for (key, result) in 
keys298
.
into_iter298
().
zip298
(
results298
.
iter_mut298
()) {
367
333
            let maybe_entry = if peek {
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [True: 0, False: 213]
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [Folded - Ignored]
  Branch (367:34): [True: 0, False: 25]
  Branch (367:34): [True: 2, False: 0]
  Branch (367:34): [True: 5, False: 0]
  Branch (367:34): [True: 4, False: 0]
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [True: 0, False: 1]
  Branch (367:34): [True: 0, False: 3]
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [True: 0, False: 0]
  Branch (367:34): [Folded - Ignored]
  Branch (367:34): [True: 0, False: 80]
368
11
                state.lru.peek_mut(key.borrow())
369
            } else {
370
322
                state.lru.get_mut(key.borrow())
371
            };
372
333
            match maybe_entry {
373
183
                Some(entry) => {
374
                    // Note: We need to check eviction because the item might be expired
375
                    // based on the current time. In such case, we remove the item while
376
                    // we are here.
377
183
                    if self.should_evict(lru_len, entry, 0, u64::MAX) {
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 94]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [Folded - Ignored]
  Branch (377:24): [True: 1, False: 13]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 3]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 1]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [True: 0, False: 0]
  Branch (377:24): [Folded - Ignored]
  Branch (377:24): [True: 0, False: 71]
378
1
                        *result = None;
379
1
                        if let Some((key, eviction_item)) = state.lru.pop_entry(key.borrow()) {
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [Folded - Ignored]
  Branch (379:32): [True: 1, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [True: 0, False: 0]
  Branch (379:32): [Folded - Ignored]
  Branch (379:32): [True: 0, False: 0]
380
1
                            info!(?key, "Item expired, evicting");
381
1
                            let data = state.remove(key.borrow(), &eviction_item, false).await;
382
                            // Store data for later unref - we can't drop state here as we're still iterating
383
                            // The unref will happen after the method completes
384
                            // For now, we just do inline unref
385
1
                            data.unref().await;
386
0
                        }
387
                    } else {
388
182
                        if !peek {
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 94, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [Folded - Ignored]
  Branch (388:28): [True: 13, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 0, False: 3]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 1, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [True: 0, False: 0]
  Branch (388:28): [Folded - Ignored]
  Branch (388:28): [True: 71, False: 0]
389
179
                            entry.seconds_since_anchor =
390
179
                                self.anchor_time.elapsed().as_secs() as i32;
391
179
                        
}3
392
182
                        *result = Some(entry.data.len());
393
                    }
394
                }
395
150
                None => *result = None,
396
            }
397
        }
398
298
    }
399
400
4.49k
    pub async fn get(&self, key: &Q) -> Option<T> {
401
        // Fast path: Check if we need eviction before acquiring lock for eviction
402
4.49k
        let needs_eviction = {
403
4.49k
            let state = self.state.lock_arc();
404
4.49k
            if let Some((_, 
peek_entry4.48k
)) = state.lru.peek_lru() {
  Branch (404:20): [True: 0, False: 0]
  Branch (404:20): [True: 0, False: 0]
  Branch (404:20): [True: 4.36k, False: 4]
  Branch (404:20): [Folded - Ignored]
  Branch (404:20): [True: 1, False: 0]
  Branch (404:20): [True: 5, False: 0]
  Branch (404:20): [True: 0, False: 0]
  Branch (404:20): [True: 1, False: 0]
  Branch (404:20): [True: 0, False: 0]
  Branch (404:20): [True: 1, False: 0]
  Branch (404:20): [True: 0, False: 0]
  Branch (404:20): [True: 9, False: 0]
  Branch (404:20): [True: 0, False: 0]
  Branch (404:20): [True: 4, False: 0]
  Branch (404:20): [True: 1, False: 1]
  Branch (404:20): [Folded - Ignored]
  Branch (404:20): [True: 41, False: 0]
  Branch (404:20): [True: 57, False: 0]
405
4.48k
                self.should_evict(
406
4.48k
                    state.lru.len(),
407
4.48k
                    peek_entry,
408
4.48k
                    state.sum_store_size,
409
4.48k
                    self.max_bytes,
410
                )
411
            } else {
412
5
                false
413
            }
414
        };
415
416
        // Perform eviction if needed
417
4.49k
        if needs_eviction {
  Branch (417:12): [True: 0, False: 0]
  Branch (417:12): [True: 0, False: 0]
  Branch (417:12): [True: 0, False: 4.37k]
  Branch (417:12): [Folded - Ignored]
  Branch (417:12): [True: 0, False: 1]
  Branch (417:12): [True: 2, False: 3]
  Branch (417:12): [True: 0, False: 0]
  Branch (417:12): [True: 0, False: 1]
  Branch (417:12): [True: 0, False: 0]
  Branch (417:12): [True: 0, False: 1]
  Branch (417:12): [True: 0, False: 0]
  Branch (417:12): [True: 0, False: 9]
  Branch (417:12): [True: 0, False: 0]
  Branch (417:12): [True: 0, False: 4]
  Branch (417:12): [True: 0, False: 2]
  Branch (417:12): [Folded - Ignored]
  Branch (417:12): [True: 0, False: 41]
  Branch (417:12): [True: 0, False: 57]
418
2
            let items_to_unref = {
419
2
                let mut state = self.state.lock_arc();
420
2
                self.evict_items(&mut *state).await
421
            };
422
            // Unref items outside of lock
423
4
            for 
item2
in items_to_unref {
424
2
                item.unref().await;
425
            }
426
4.49k
        }
427
428
        // Now get the item
429
4.49k
        let mut state = self.state.lock_arc();
430
4.49k
        let 
entry4.48k
= state.lru.get_mut(key.borrow())
?10
;
431
4.48k
        entry.seconds_since_anchor = self.anchor_time.elapsed().as_secs() as i32;
432
4.48k
        Some(entry.data.clone())
433
4.49k
    }
434
435
    /// Returns the replaced item if any.
436
5.93k
    pub async fn insert(&self, key: K, data: T) -> Option<T>
437
5.93k
    where
438
5.93k
        K: 'static,
439
5.93k
    {
440
5.93k
        self.insert_with_time(key, data, self.anchor_time.elapsed().as_secs() as i32)
441
5.93k
            .await
442
5.93k
    }
443
444
    /// Returns the replaced item if any.
445
5.93k
    pub async fn insert_with_time(&self, key: K, data: T, seconds_since_anchor: i32) -> Option<T> {
446
5.93k
        let items_to_unref = {
447
5.93k
            let mut state = self.state.lock_arc();
448
5.93k
            self.inner_insert_many(&mut state, [(key, data)], seconds_since_anchor)
449
5.93k
                .await
450
        };
451
452
        // Unref items outside of lock
453
5.93k
        let mut results = Vec::new();
454
5.99k
        for 
item54
in items_to_unref {
455
54
            item.unref().await;
456
54
            results.push(item);
457
        }
458
459
5.93k
        results.into_iter().next()
460
5.93k
    }
461
462
    /// Same as `insert()`, but optimized for multiple inserts.
463
    /// Returns the replaced items if any.
464
7
    pub async fn insert_many<It>(&self, inserts: It) -> Vec<T>
465
7
    where
466
7
        It: IntoIterator<Item = (K, T)> + Send,
467
7
        // Note: It's not enough to have the inserts themselves be Send. The
468
7
        // returned iterator should be Send as well.
469
7
        <It as IntoIterator>::IntoIter: Send,
470
7
        K: 'static,
471
7
    {
472
7
        let mut inserts = inserts.into_iter().peekable();
473
        // Shortcut for cases where there are no inserts, so we don't need to lock.
474
7
        if inserts.peek().is_none() {
  Branch (474:12): [True: 0, False: 0]
  Branch (474:12): [Folded - Ignored]
  Branch (474:12): [True: 1, False: 1]
  Branch (474:12): [True: 4, False: 1]
  Branch (474:12): [Folded - Ignored]
475
5
            return Vec::new();
476
2
        }
477
478
2
        let items_to_unref = {
479
2
            let state = &mut self.state.lock_arc();
480
2
            self.inner_insert_many(state, inserts, self.anchor_time.elapsed().as_secs() as i32)
481
2
                .await
482
        };
483
484
        // Unref items outside of lock
485
2
        let mut results = Vec::new();
486
2
        for 
item0
in items_to_unref {
487
0
            item.unref().await;
488
0
            results.push(item);
489
        }
490
491
2
        results
492
7
    }
493
494
5.94k
    async fn inner_insert_many<It>(
495
5.94k
        &self,
496
5.94k
        state: &mut State<K, Q, T>,
497
5.94k
        inserts: It,
498
5.94k
        seconds_since_anchor: i32,
499
5.94k
    ) -> Vec<T>
500
5.94k
    where
501
5.94k
        It: IntoIterator<Item = (K, T)> + Send,
502
5.94k
        // Note: It's not enough to have the inserts themselves be Send. The
503
5.94k
        // returned iterator should be Send as well.
504
5.94k
        <It as IntoIterator>::IntoIter: Send,
505
5.94k
    {
506
5.94k
        let mut replaced_items = Vec::new();
507
11.8k
        for (
key5.94k
,
data5.94k
) in inserts {
508
5.94k
            let new_item_size = data.len();
509
5.94k
            let eviction_item = EvictionItem {
510
5.94k
                seconds_since_anchor,
511
5.94k
                data,
512
5.94k
            };
513
514
5.94k
            if let Some(
old_item40
) = state.put(&key, eviction_item).await {
  Branch (514:20): [True: 0, False: 0]
  Branch (514:20): [True: 18, False: 5.73k]
  Branch (514:20): [True: 0, False: 0]
  Branch (514:20): [True: 0, False: 0]
  Branch (514:20): [True: 0, False: 0]
  Branch (514:20): [Folded - Ignored]
  Branch (514:20): [True: 1, False: 1]
  Branch (514:20): [True: 0, False: 27]
  Branch (514:20): [True: 0, False: 3]
  Branch (514:20): [True: 0, False: 0]
  Branch (514:20): [True: 0, False: 1]
  Branch (514:20): [True: 0, False: 4]
  Branch (514:20): [True: 0, False: 1]
  Branch (514:20): [True: 0, False: 2]
  Branch (514:20): [True: 0, False: 2]
  Branch (514:20): [True: 1, False: 1]
  Branch (514:20): [True: 1, False: 1]
  Branch (514:20): [True: 0, False: 1]
  Branch (514:20): [True: 3, False: 13]
  Branch (514:20): [True: 0, False: 0]
  Branch (514:20): [True: 0, False: 2]
  Branch (514:20): [Folded - Ignored]
  Branch (514:20): [True: 16, False: 81]
  Branch (514:20): [True: 0, False: 27]
515
40
                replaced_items.push(old_item);
516
5.90k
            }
517
5.94k
            state.sum_store_size += new_item_size;
518
5.94k
            state.lifetime_inserted_bytes.add(new_item_size);
519
        }
520
521
        // Perform eviction after all insertions
522
5.94k
        let items_to_unref = self.evict_items(state).await;
523
524
        // Note: We cannot drop the state lock here since we're borrowing it,
525
        // but the caller will handle unreffing these items after releasing the lock
526
5.95k
        for 
item14
in items_to_unref {
527
14
            replaced_items.push(item);
528
14
        }
529
530
5.94k
        replaced_items
531
5.94k
    }
532
533
15
    pub async fn remove(&self, key: &Q) -> bool {
534
15
        let (items_to_unref, removed_item) = {
535
15
            let mut state = self.state.lock_arc();
536
537
            // First perform eviction
538
15
            let evicted_items = self.evict_items(&mut *state).await;
539
540
            // Then try to remove the requested item
541
15
            let removed = if let Some(
entry14
) = state.lru.pop(key.borrow()) {
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [Folded - Ignored]
  Branch (541:34): [True: 6, False: 0]
  Branch (541:34): [True: 1, False: 0]
  Branch (541:34): [True: 1, False: 1]
  Branch (541:34): [True: 1, False: 0]
  Branch (541:34): [True: 1, False: 0]
  Branch (541:34): [True: 2, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [True: 2, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [True: 0, False: 0]
  Branch (541:34): [Folded - Ignored]
  Branch (541:34): [True: 0, False: 0]
542
14
                Some(state.remove(key, &entry, false).await)
543
            } else {
544
1
                None
545
            };
546
547
15
            (evicted_items, removed)
548
        };
549
550
        // Unref evicted items outside of lock
551
16
        for 
item1
in items_to_unref {
552
1
            item.unref().await;
553
        }
554
555
        // Unref removed item if any
556
15
        if let Some(
item14
) = removed_item {
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [Folded - Ignored]
  Branch (556:16): [True: 6, False: 0]
  Branch (556:16): [True: 1, False: 0]
  Branch (556:16): [True: 1, False: 1]
  Branch (556:16): [True: 1, False: 0]
  Branch (556:16): [True: 1, False: 0]
  Branch (556:16): [True: 2, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [True: 2, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [True: 0, False: 0]
  Branch (556:16): [Folded - Ignored]
  Branch (556:16): [True: 0, False: 0]
557
14
            item.unref().await;
558
14
            return true;
559
1
        }
560
561
1
        false
562
15
    }
563
564
    /// Same as `remove()`, but allows for a conditional to be applied to the
565
    /// entry before removal in an atomic fashion.
566
1
    pub async fn remove_if<F>(&self, key: &Q, cond: F) -> bool
567
1
    where
568
1
        F: FnOnce(&T) -> bool + Send,
569
1
    {
570
1
        let mut state = self.state.lock_arc();
571
1
        if let Some(entry) = state.lru.get(key.borrow()) {
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [Folded - Ignored]
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [True: 1, False: 0]
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [True: 0, False: 0]
  Branch (571:16): [Folded - Ignored]
  Branch (571:16): [True: 0, False: 0]
572
1
            if !cond(&entry.data) {
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [Folded - Ignored]
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [True: 0, False: 1]
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [True: 0, False: 0]
  Branch (572:16): [Folded - Ignored]
  Branch (572:16): [True: 0, False: 0]
573
0
                return false;
574
1
            }
575
            // First perform eviction
576
1
            let evicted_items = self.evict_items(&mut state).await;
577
578
            // Then try to remove the requested item
579
1
            let removed_item = if let Some(entry) = state.lru.pop(key.borrow()) {
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [Folded - Ignored]
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [True: 1, False: 0]
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [True: 0, False: 0]
  Branch (579:39): [Folded - Ignored]
  Branch (579:39): [True: 0, False: 0]
580
1
                Some(state.remove(key, &entry, false).await)
581
            } else {
582
0
                None
583
            };
584
585
            // Drop the lock before unref operations
586
1
            drop(state);
587
588
            // Unref evicted items
589
1
            for 
item0
in evicted_items {
590
0
                item.unref().await;
591
            }
592
593
            // Unref removed item if any
594
1
            if let Some(item) = removed_item {
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [Folded - Ignored]
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [True: 1, False: 0]
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [True: 0, False: 0]
  Branch (594:20): [Folded - Ignored]
  Branch (594:20): [True: 0, False: 0]
595
1
                item.unref().await;
596
1
                return true;
597
0
            }
598
599
0
            return false;
600
0
        }
601
0
        false
602
1
    }
603
604
5
    pub fn add_remove_callback(&self, callback: Box<dyn RemoveStateCallback<Q>>) {
605
5
        self.state.lock_arc().add_remove_callback(callback);
606
5
    }
607
}