clock_page_replacement/
lib.rs1#![no_std]
6
7extern crate alloc;
8
9use alloc::collections::VecDeque;
10
11pub trait Page {
13 fn is_valid(&self) -> bool;
15
16 fn is_accessed(&self) -> bool;
18
19 fn is_dirty(&self) -> bool;
21
22 fn set_accessed(&mut self);
24
25 fn set_dirty(&mut self);
27
28 fn clear_accessed(&mut self);
30
31 fn clear_dirty(&mut self);
33}
34
35#[derive(Default)]
51pub struct ClockPageReplacer<T: Page> {
52 pages: VecDeque<T>,
53}
54
55impl<T: Page> ClockPageReplacer<T> {
56 pub fn new() -> Self {
58 Self {
59 pages: VecDeque::new(),
60 }
61 }
62
63 pub fn register(&mut self, page: T) {
65 self.pages.push_back(page);
66 }
67
68 pub fn replace(&mut self) -> Option<T> {
75 loop {
76 let mut page = self.pages.pop_front()?;
77
78 if !page.is_valid() {
80 continue;
81 }
82
83 if !page.is_accessed() && !page.is_dirty() {
84 return Some(page);
85 } else if page.is_accessed() {
86 page.clear_accessed();
87 } else if page.is_dirty() {
88 page.clear_dirty();
89 } else {
90 unreachable!("Logically unreachable");
91 }
92
93 self.pages.push_back(page);
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 extern crate std;
102 use std::{cell::RefCell, collections::BTreeSet, println, rc::Rc, vec::Vec};
103
104 use super::*;
105
106 #[derive(Clone, Debug)]
108 struct TestPage {
109 id: usize,
110 valid: bool,
111 accessed: bool,
112 dirty: bool,
113 }
114
115 impl TestPage {
116 fn new(id: usize, valid: bool) -> Self {
117 Self {
118 id,
119 valid,
120 accessed: false,
121 dirty: false,
122 }
123 }
124 }
125
126 impl Drop for TestPage {
127 fn drop(&mut self) {
128 println!("Page {} dropped", self.id);
129 }
130 }
131
132 impl Page for Rc<RefCell<TestPage>> {
133 fn is_valid(&self) -> bool {
134 self.borrow().valid
135 }
136
137 fn is_accessed(&self) -> bool {
138 self.borrow().accessed
139 }
140
141 fn is_dirty(&self) -> bool {
142 self.borrow().dirty
143 }
144
145 fn set_accessed(&mut self) {
146 let mut page = self.borrow_mut();
147 page.accessed = true;
148 println!(
149 "Page {} accessed, current AD bits: {} {}",
150 page.id, page.accessed, page.dirty
151 );
152 }
153
154 fn set_dirty(&mut self) {
155 let mut page = self.borrow_mut();
156 page.dirty = true;
157 println!(
158 "Page {} dirty, current AD bits: {} {}",
159 page.id, page.accessed, page.dirty
160 );
161 }
162
163 fn clear_accessed(&mut self) {
164 let mut page = self.borrow_mut();
165 page.accessed = false;
166 println!(
167 "Page {} accessed cleared, current AD bits: {} {}",
168 page.id, page.accessed, page.dirty
169 );
170 }
171
172 fn clear_dirty(&mut self) {
173 let mut page = self.borrow_mut();
174 page.dirty = false;
175 println!(
176 "Page {} dirty cleared, current AD bits: {} {}",
177 page.id, page.accessed, page.dirty
178 );
179 }
180 }
181
182 struct MemoryAccess {
184 page_id: usize,
185 write: bool,
186 }
187
188 impl From<(usize, bool)> for MemoryAccess {
189 fn from((page_id, write): (usize, bool)) -> Self {
190 Self { page_id, write }
191 }
192 }
193
194 #[test]
196 fn test_basic() {
197 let mut replacer = ClockPageReplacer::new();
198
199 for i in 0..10 {
200 let page = TestPage {
201 id: i,
202 valid: i % 5 != 0,
203 accessed: i % 2 == 0,
204 dirty: i % 3 == 0,
205 };
206 println!("Registered page {page:?}");
207 replacer.register(Rc::new(RefCell::new(page)));
208 }
209
210 for _ in 0..10 {
211 if let Some(page) = replacer.replace() {
212 let page = page.borrow();
213 println!("Replaced page {page:?}");
214 assert_eq!(page.accessed, false);
215 assert_eq!(page.dirty, false);
216 } else {
217 println!("No page to replace");
218 }
219 }
220 }
221
222 #[test]
226 fn test_example() {
227 const MEM_SIZE: usize = 4;
228 let mem = (0..5)
229 .map(|i| Rc::new(RefCell::new(TestPage::new(i, i != 4))))
230 .collect::<Vec<_>>();
231 let mem_accesses = [
232 (2, false),
233 (0, true),
234 (3, false),
235 (1, true),
236 (4, false),
237 (1, false),
238 (0, true),
239 (1, false),
240 (2, false),
241 (3, false),
242 ]
243 .into_iter()
244 .map(Into::into)
245 .collect::<Vec<MemoryAccess>>();
246
247 let mut working_set: BTreeSet<_> = (0..4).collect();
248 let mut replacer = ClockPageReplacer::new();
249
250 for i in 0..4 {
251 replacer.register(mem[i].clone());
252 }
253
254 for access in mem_accesses {
256 println!("Accessing page {}, write: {}", access.page_id, access.write);
257
258 if !working_set.contains(&access.page_id) {
259 println!(
260 "Page {} is not in the working set, loading it",
261 access.page_id
262 );
263 if working_set.len() == MEM_SIZE {
264 println!("Working set is full, replacing a page");
265 let replaced_page = replacer.replace().unwrap();
266 replacer.register(mem[access.page_id].clone());
267
268 println!("Replacing page {:?}", replaced_page);
269 let replaced_id = replaced_page.borrow().id;
270 working_set.remove(&replaced_id);
271 replaced_page.borrow_mut().valid = false;
272
273 working_set.insert(access.page_id);
274 mem[access.page_id].borrow_mut().valid = true;
275 }
276 }
277 assert!(working_set.contains(&access.page_id));
278
279 let mut page = mem[access.page_id].clone();
280 assert!(page.is_valid());
281
282 if access.write {
283 page.set_dirty();
284 }
285 page.set_accessed();
286 }
287 }
288}