rsdb/ds/
lru.rs

1use std::ptr;
2use std::sync::Mutex;
3
4use super::*;
5
6pub struct Lru {
7    shards: Vec<Mutex<Shard>>,
8}
9
10impl Lru {
11    /// Instantiates a new `Lru` cache.
12    pub fn new(cache_capacity: usize, cache_bits: usize) -> Lru {
13        assert!(
14            cache_bits <= 20,
15            "way too many shards. use a smaller number of cache_bits"
16        );
17        let size = 1 << cache_bits;
18        let shard_capacity = cache_capacity / size;
19
20        Lru {
21            shards: rep_no_copy![Mutex::new(Shard::new(shard_capacity)); size],
22        }
23    }
24
25    /// Called when a page is accessed. Returns a Vec of pages to
26    /// try to page-out. For each one of these, the caller is expected
27    /// to call `page_out_succeeded` if the page-out succeeded.
28    pub fn accessed(&self, pid: PageID, sz: usize) -> Vec<PageID> {
29        let shard_idx = pid % self.shards.len();
30        let rel_idx = pid / self.shards.len();
31        let shard_mu = &self.shards[shard_idx];
32        let mut shard = shard_mu.lock().unwrap();
33        let mut rel_ids = shard.accessed(rel_idx, sz);
34
35        for rel_id in &mut rel_ids {
36            let real_id = (*rel_id * self.shards.len()) + shard_idx;
37            *rel_id = real_id;
38        }
39
40        rel_ids
41    }
42}
43
44#[derive(Clone)]
45struct Entry {
46    ptr: *mut dll::Node,
47    sz: usize,
48}
49
50impl Default for Entry {
51    fn default() -> Entry {
52        Entry {
53            ptr: ptr::null_mut(),
54            sz: 0,
55        }
56    }
57}
58
59struct Shard {
60    list: Dll,
61    entries: Vec<Entry>,
62    capacity: usize,
63    sz: usize,
64}
65
66impl Shard {
67    fn new(capacity: usize) -> Shard {
68        Shard {
69            list: Dll::default(),
70            entries: vec![],
71            capacity: capacity,
72            sz: 0,
73        }
74    }
75
76    fn accessed(&mut self, rel_idx: PageID, sz: usize) -> Vec<PageID> {
77        if self.entries.len() <= rel_idx {
78            self.entries.resize(rel_idx + 1, Entry::default());
79        }
80
81        {
82            let entry = &mut self.entries[rel_idx];
83
84            self.sz -= entry.sz;
85            entry.sz = sz;
86            self.sz += sz;
87
88            if entry.ptr.is_null() {
89                entry.ptr = self.list.push_head(rel_idx);
90            } else {
91                entry.ptr = self.list.promote(entry.ptr);
92            }
93        }
94
95        let mut to_evict = vec![];
96        while self.sz > self.capacity {
97            if self.list.len() == 1 {
98                // don't evict what we just added
99                break;
100            }
101
102            let min_pid = self.list.pop_tail().unwrap();
103            self.entries[min_pid].ptr = ptr::null_mut();
104
105            to_evict.push(min_pid);
106
107            self.sz -= self.entries[min_pid].sz;
108            self.entries[min_pid].sz = 0;
109        }
110
111        to_evict
112    }
113}