1use crate::buffer::BufferPoolManager;
2use crate::recovery::Lsn;
3use derive_with::With;
4use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
5use std::mem::{self, ManuallyDrop};
6use std::ops::{Deref, DerefMut};
7use std::sync::atomic::{AtomicU32, Ordering};
8use std::sync::Arc;
9
10pub type PageId = u32;
11pub type AtomicPageId = AtomicU32;
12
13pub const INVALID_PAGE_ID: PageId = 0;
14pub const PAGE_SIZE: usize = 4096;
15
16#[derive(Debug, With)]
17pub struct Page {
18 pub page_id: PageId,
19 pub data: [u8; PAGE_SIZE],
20 pub pin_count: AtomicU32, pub is_dirty: bool,
22 pub page_lsn: Lsn,
23}
24
25impl Page {
26 pub fn empty() -> Self {
27 Self {
28 page_id: INVALID_PAGE_ID,
29 data: [0; PAGE_SIZE],
30 pin_count: AtomicU32::new(0), is_dirty: false,
32 page_lsn: 0,
33 }
34 }
35
36 pub fn new(page_id: PageId) -> Self {
37 Self {
38 page_id,
39 data: [0; PAGE_SIZE],
40 pin_count: AtomicU32::new(0),
41 is_dirty: false,
42 page_lsn: 0,
43 }
44 }
45
46 pub fn page_id(&self) -> PageId {
48 self.page_id
49 }
50
51 pub fn data(&self) -> &[u8; PAGE_SIZE] {
53 &self.data
54 }
55
56 pub fn destroy(&mut self) {
58 self.page_id = INVALID_PAGE_ID;
59 self.data = [0; PAGE_SIZE];
60 self.pin_count.store(0, Ordering::Relaxed);
61 self.is_dirty = false;
62 self.page_lsn = 0;
63 }
64
65 pub fn pin(&self) -> u32 {
67 self.pin_count.fetch_add(1, Ordering::AcqRel) + 1
68 }
69
70 pub fn unpin(&self) -> u32 {
72 self.pin_count.fetch_sub(1, Ordering::AcqRel)
73 }
74
75 pub fn get_pin_count(&self) -> u32 {
77 self.pin_count.load(Ordering::Acquire)
78 }
79
80 pub fn lsn(&self) -> Lsn {
81 self.page_lsn
82 }
83
84 pub fn set_lsn(&mut self, lsn: Lsn) {
85 self.page_lsn = lsn;
86 }
87}
88
89#[derive(Debug)]
93pub struct ReadPageGuard {
94 bpm: Arc<BufferPoolManager>,
95 _page: Arc<RwLock<Page>>,
98 guard: ManuallyDrop<RwLockReadGuard<'static, Page>>,
99}
100
101impl Deref for ReadPageGuard {
102 type Target = Page;
103 fn deref(&self) -> &Self::Target {
104 &self.guard
105 }
106}
107
108impl ReadPageGuard {
110 pub fn pin_count(&self) -> u32 {
111 self.guard.get_pin_count()
112 }
113}
114
115impl Drop for ReadPageGuard {
116 fn drop(&mut self) {
117 let old_pin = self.guard.unpin();
119 let page_id = self.guard.page_id;
120 let is_dirty = self.guard.is_dirty;
121 unsafe {
122 ManuallyDrop::drop(&mut self.guard);
123 }
124 if let Err(e) = self.bpm.complete_unpin(page_id, is_dirty, old_pin, None) {
125 eprintln!("Warning: Failed to complete_unpin page {}: {}", page_id, e);
126 }
127 }
128}
129
130#[derive(Debug)]
134pub struct WritePageGuard {
135 bpm: Arc<BufferPoolManager>,
136 _page: Arc<RwLock<Page>>,
137 guard: ManuallyDrop<RwLockWriteGuard<'static, Page>>,
138 first_dirty_lsn: Option<Lsn>,
140}
141
142impl Deref for WritePageGuard {
143 type Target = Page;
144 fn deref(&self) -> &Self::Target {
145 &self.guard
146 }
147}
148
149impl DerefMut for WritePageGuard {
150 fn deref_mut(&mut self) -> &mut Self::Target {
151 self.guard.is_dirty = true;
153 &mut self.guard
154 }
155}
156
157impl WritePageGuard {
159 pub fn pin_count(&self) -> u32 {
160 self.guard.get_pin_count()
161 }
162
163 pub fn overwrite(&mut self, data: &[u8], new_lsn: Option<Lsn>) {
164 debug_assert_eq!(data.len(), PAGE_SIZE);
165 self.guard.data.copy_from_slice(data);
166 if let Some(lsn) = new_lsn {
167 self.guard.page_lsn = lsn;
168 if self.first_dirty_lsn.is_none() {
169 self.first_dirty_lsn = Some(lsn);
170 }
171 }
172 self.guard.is_dirty = true;
173 }
174}
175
176impl Drop for WritePageGuard {
177 fn drop(&mut self) {
178 let old_pin = self.guard.unpin();
180 let page_id = self.guard.page_id;
181 let is_dirty = self.guard.is_dirty;
182 let lsn = self.guard.page_lsn;
183 unsafe {
184 ManuallyDrop::drop(&mut self.guard);
185 }
186 let rec_lsn_hint = if self.first_dirty_lsn.is_some() {
187 self.first_dirty_lsn
188 } else if is_dirty {
189 Some(lsn)
190 } else {
191 None
192 };
193 if let Err(e) = self
194 .bpm
195 .complete_unpin(page_id, is_dirty, old_pin, rec_lsn_hint)
196 {
197 eprintln!("Warning: Failed to complete_unpin page {}: {}", page_id, e);
198 }
199 }
200}
201
202pub(crate) fn new_read_guard(
204 bpm: Arc<BufferPoolManager>,
205 page: Arc<RwLock<Page>>,
206) -> ReadPageGuard {
207 let guard = page.read();
208 let static_guard = unsafe { mem::transmute::<_, RwLockReadGuard<'static, Page>>(guard) };
212 ReadPageGuard {
213 bpm,
214 _page: page,
215 guard: ManuallyDrop::new(static_guard),
216 }
217}
218
219pub(crate) fn new_write_guard(
220 bpm: Arc<BufferPoolManager>,
221 page: Arc<RwLock<Page>>,
222) -> WritePageGuard {
223 let guard = page.write();
224 let static_guard = unsafe { mem::transmute::<_, RwLockWriteGuard<'static, Page>>(guard) };
226 WritePageGuard {
227 bpm,
228 _page: page,
229 guard: ManuallyDrop::new(static_guard),
230 first_dirty_lsn: None,
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use std::sync::Arc;
237 use tempfile::TempDir;
238
239 use crate::storage::disk_manager::DiskManager;
241 use crate::storage::disk_scheduler::DiskScheduler;
242 use crate::{buffer::buffer_pool::BufferPoolManager, utils::cache::Replacer};
243
244 use super::{Page, INVALID_PAGE_ID};
246
247 fn setup_real_bpm_environment(num_pages: usize) -> (TempDir, Arc<BufferPoolManager>) {
249 let temp_dir = TempDir::new().unwrap();
250 let db_path = temp_dir.path().join("test.db");
251
252 let disk_manager = Arc::new(DiskManager::try_new(db_path).unwrap());
253 let disk_scheduler = Arc::new(DiskScheduler::new(disk_manager));
254 let buffer_pool_manager = Arc::new(BufferPoolManager::new(num_pages, disk_scheduler));
255
256 (temp_dir, buffer_pool_manager)
257 }
258
259 #[test]
260 fn test_page_struct_creation() {
261 let page = Page::new(1);
262 assert_eq!(page.page_id(), 1);
263 assert!(!page.is_dirty);
264 assert_eq!(page.get_pin_count(), 0);
265 assert!(page.data().iter().all(|&b| b == 0));
266
267 let empty_page = Page::empty();
268 assert_eq!(empty_page.page_id(), INVALID_PAGE_ID);
269 }
270
271 #[test]
272 fn test_read_guard_deref_and_drop() {
273 let (_temp_dir, bpm) = setup_real_bpm_environment(10);
274
275 let page_id = {
276 let guard = bpm.new_page().unwrap();
277 guard.page_id()
278 };
279
280 assert_eq!(bpm.replacer.read().size(), 1);
282
283 {
284 let read_guard = bpm.fetch_page_read(page_id).unwrap();
285
286 assert_eq!(read_guard.page_id(), page_id);
288 assert_eq!(read_guard.pin_count(), 1);
289
290 assert_eq!(bpm.replacer.read().size(), 0);
292
293 }
295
296 assert_eq!(bpm.replacer.read().size(), 1);
298
299 let final_check_guard = bpm.fetch_page_read(page_id).unwrap();
301 assert_eq!(final_check_guard.pin_count(), 1);
303 }
304
305 #[test]
306 fn test_write_guard_deref_mut_and_drop() {
307 let (_temp_dir, bpm) = setup_real_bpm_environment(10);
308 let page_id;
309
310 {
311 let mut write_guard = bpm.new_page().unwrap();
312 page_id = write_guard.page_id();
313
314 write_guard.data[0] = 123;
316
317 assert!(write_guard.is_dirty);
319
320 assert_eq!(bpm.replacer.read().size(), 0);
322
323 }
325
326 assert_eq!(bpm.replacer.read().size(), 1);
328
329 let read_guard = bpm.fetch_page_read(page_id).unwrap();
331 assert_eq!(read_guard.data()[0], 123);
332 assert!(read_guard.is_dirty); }
334
335 #[test]
336 fn test_write_guard_without_mutation_is_not_dirty() {
337 let (_temp_dir, bpm) = setup_real_bpm_environment(10);
338 let page_id = bpm.new_page().unwrap().page_id(); {
341 let write_guard = bpm.fetch_page_write(page_id).unwrap();
343 assert_eq!(write_guard.page_id(), page_id);
345 }
347
348 let read_guard = bpm.fetch_page_read(page_id).unwrap();
350 assert!(!read_guard.is_dirty);
351 }
352
353 #[test]
354 fn test_guards_hold_lock() {
355 let (_temp_dir, bpm) = setup_real_bpm_environment(10);
356 let page_arc = bpm.pool[0].clone(); let page_id = 100;
360 bpm.page_table.insert(page_id, 0);
361 *page_arc.write() = Page::new(page_id);
362
363 let mut write_guard = bpm.fetch_page_write(page_id).unwrap();
365
366 assert!(page_arc.try_read().is_none());
368 assert!(page_arc.try_write().is_none());
369
370 write_guard.data[0] = 123;
372
373 drop(write_guard);
375
376 let read_guard = bpm.fetch_page_read(page_id).unwrap();
378 assert!(page_arc.try_write().is_none()); assert_eq!(read_guard.data[0], 123);
380 }
381}