1use crate::buffer::buffer_pool::{BufferPool, FrameMeta};
2use crate::buffer::{BufferManager, FrameId};
3use crate::recovery::Lsn;
4use derive_with::With;
5use parking_lot::{RwLockReadGuard, RwLockWriteGuard};
6use std::mem::{self, ManuallyDrop};
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 PageMeta {
18 pub page_id: PageId,
19 pub pin_count: AtomicU32,
20 pub is_dirty: bool,
21 pub page_lsn: Lsn,
22}
23
24impl PageMeta {
25 pub fn empty() -> Self {
26 Self {
27 page_id: INVALID_PAGE_ID,
28 pin_count: AtomicU32::new(0),
29 is_dirty: false,
30 page_lsn: 0,
31 }
32 }
33
34 pub fn new(page_id: PageId) -> Self {
35 Self {
36 page_id,
37 pin_count: AtomicU32::new(0),
38 is_dirty: false,
39 page_lsn: 0,
40 }
41 }
42
43 pub fn destroy(&mut self) {
44 self.page_id = INVALID_PAGE_ID;
45 self.pin_count.store(0, Ordering::Relaxed);
46 self.is_dirty = false;
47 self.page_lsn = 0;
48 }
49}
50
51#[derive(Debug)]
52pub struct ReadPageGuard {
53 bpm: Arc<BufferManager>,
54 pool: Arc<BufferPool>,
55 frame_id: FrameId,
56 guard: ManuallyDrop<RwLockReadGuard<'static, ()>>,
57}
58
59impl ReadPageGuard {
60 pub fn pin_count(&self) -> u32 {
61 self.meta_snapshot().pin_count
62 }
63
64 pub fn data(&self) -> &[u8] {
65 unsafe { self.pool.frame_slice(self.frame_id) }
66 }
67
68 pub fn is_dirty(&self) -> bool {
69 self.meta_snapshot().is_dirty
70 }
71
72 pub fn page_id(&self) -> PageId {
73 self.meta_snapshot().page_id
74 }
75
76 pub fn lsn(&self) -> Lsn {
77 self.meta_snapshot().lsn
78 }
79
80 pub fn meta_snapshot(&self) -> FrameMeta {
81 self.pool.frame_meta(self.frame_id).clone()
82 }
83
84 pub fn frame_id(&self) -> FrameId {
85 self.frame_id
86 }
87}
88
89impl Drop for ReadPageGuard {
90 fn drop(&mut self) {
91 let snapshot = self.meta_snapshot();
92 let page_id = snapshot.page_id;
93 let is_dirty = snapshot.is_dirty;
94 unsafe {
95 ManuallyDrop::drop(&mut self.guard);
96 }
97 if let Err(e) = self.bpm.complete_unpin(page_id, is_dirty, None) {
98 eprintln!("Warning: Failed to complete_unpin page {}: {}", page_id, e);
99 }
100 }
101}
102
103#[derive(Debug)]
104pub struct WritePageGuard {
105 bpm: Arc<BufferManager>,
106 pool: Arc<BufferPool>,
107 frame_id: FrameId,
108 guard: ManuallyDrop<RwLockWriteGuard<'static, ()>>,
109 first_dirty_lsn: Option<Lsn>,
110}
111
112impl WritePageGuard {
113 pub fn pin_count(&self) -> u32 {
114 self.meta_snapshot().pin_count
115 }
116
117 pub fn data(&self) -> &[u8] {
118 unsafe { self.pool.frame_slice(self.frame_id) }
119 }
120
121 pub fn data_mut(&mut self) -> &mut [u8] {
122 unsafe { self.pool.frame_slice_mut(self.frame_id) }
123 }
124
125 pub fn is_dirty(&self) -> bool {
126 self.meta_snapshot().is_dirty
127 }
128
129 pub fn page_id(&self) -> PageId {
130 self.meta_snapshot().page_id
131 }
132
133 pub fn lsn(&self) -> Lsn {
134 self.meta_snapshot().lsn
135 }
136
137 pub fn set_lsn(&mut self, lsn: Lsn) {
138 let mut meta = self.pool.frame_meta(self.frame_id);
139 meta.lsn = lsn;
140 if self.first_dirty_lsn.is_none() {
141 self.first_dirty_lsn = Some(lsn);
142 }
143 }
144
145 pub fn mark_dirty(&mut self) {
146 let mut meta = self.pool.frame_meta(self.frame_id);
147 meta.is_dirty = true;
148 if self.first_dirty_lsn.is_none() {
149 self.first_dirty_lsn = Some(meta.lsn);
150 }
151 }
152
153 pub fn overwrite(&mut self, data: &[u8], new_lsn: Option<Lsn>) {
154 debug_assert_eq!(data.len(), PAGE_SIZE);
155 let slice = unsafe { self.pool.frame_slice_mut(self.frame_id) };
156 slice.copy_from_slice(data);
157 if let Some(lsn) = new_lsn {
158 self.set_lsn(lsn);
159 }
160 self.mark_dirty();
161 }
162
163 pub fn meta_snapshot(&self) -> FrameMeta {
164 self.pool.frame_meta(self.frame_id).clone()
165 }
166
167 pub fn frame_id(&self) -> FrameId {
168 self.frame_id
169 }
170}
171
172impl Drop for WritePageGuard {
173 fn drop(&mut self) {
174 let snapshot = self.meta_snapshot();
175 let page_id = snapshot.page_id;
176 let is_dirty = snapshot.is_dirty;
177 let lsn = snapshot.lsn;
178 unsafe {
179 ManuallyDrop::drop(&mut self.guard);
180 }
181 let rec_lsn_hint = if let Some(first) = self.first_dirty_lsn {
182 Some(first)
183 } else if is_dirty {
184 Some(lsn)
185 } else {
186 None
187 };
188 if let Err(e) = self.bpm.complete_unpin(page_id, is_dirty, rec_lsn_hint) {
189 eprintln!("Warning: Failed to complete_unpin page {}: {}", page_id, e);
190 }
191 }
192}
193
194pub(crate) fn new_read_guard(bpm: Arc<BufferManager>, frame_id: FrameId) -> ReadPageGuard {
195 let pool = bpm.buffer_pool();
196 let lock = pool.frame_lock(frame_id).read();
197 let static_guard =
198 unsafe { mem::transmute::<RwLockReadGuard<'_, ()>, RwLockReadGuard<'static, ()>>(lock) };
199 ReadPageGuard {
200 bpm,
201 pool,
202 frame_id,
203 guard: ManuallyDrop::new(static_guard),
204 }
205}
206
207pub(crate) fn new_write_guard(bpm: Arc<BufferManager>, frame_id: FrameId) -> WritePageGuard {
208 let pool = bpm.buffer_pool();
209 let lock = pool.frame_lock(frame_id).write();
210 let static_guard =
211 unsafe { mem::transmute::<RwLockWriteGuard<'_, ()>, RwLockWriteGuard<'static, ()>>(lock) };
212 WritePageGuard {
213 bpm,
214 pool,
215 frame_id,
216 guard: ManuallyDrop::new(static_guard),
217 first_dirty_lsn: None,
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use std::sync::atomic::Ordering;
224 use std::sync::Arc;
225 use tempfile::TempDir;
226
227 use crate::buffer::BufferManager;
228 use crate::storage::disk_manager::DiskManager;
229 use crate::storage::disk_scheduler::DiskScheduler;
230
231 use super::{PageMeta, INVALID_PAGE_ID, PAGE_SIZE};
232
233 fn setup_real_bpm_environment(num_pages: usize) -> (TempDir, Arc<BufferManager>) {
234 let temp_dir = TempDir::new().unwrap();
235 let db_path = temp_dir.path().join("test.db");
236
237 let disk_manager = Arc::new(DiskManager::try_new(db_path).unwrap());
238 let disk_scheduler = Arc::new(DiskScheduler::new(disk_manager));
239 let buffer_pool_manager = Arc::new(BufferManager::new(num_pages, disk_scheduler));
240
241 (temp_dir, buffer_pool_manager)
242 }
243
244 #[test]
245 fn test_page_struct_creation() {
246 let page = PageMeta::new(1);
247 assert_eq!(page.page_id, 1);
248 assert!(!page.is_dirty);
249 assert_eq!(page.pin_count.load(Ordering::Acquire), 0);
250
251 let empty_page = PageMeta::empty();
252 assert_eq!(empty_page.page_id, INVALID_PAGE_ID);
253 }
254
255 #[test]
256 fn test_read_guard_deref_and_drop() {
257 let (_temp_dir, bpm) = setup_real_bpm_environment(10);
258
259 let (page_id, frame_id) = {
260 let guard = bpm.new_page().unwrap();
261 let frame_id = guard.frame_id();
262 (guard.page_id(), frame_id)
263 };
264
265 {
266 let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
267 assert_eq!(meta.pin_count, 0);
268 }
269
270 let read_guard = bpm.fetch_page_read(page_id).unwrap();
271 assert_eq!(read_guard.page_id(), page_id);
272 assert_eq!(read_guard.pin_count(), 1);
273 assert_eq!(read_guard.data().len(), PAGE_SIZE);
274 let snapshot = read_guard.meta_snapshot();
275 assert_eq!(snapshot.pin_count, 1);
276 drop(read_guard);
277
278 let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
279 assert_eq!(meta.pin_count, 0);
280 }
281
282 #[test]
283 fn test_write_guard_deref_mut_and_drop() {
284 let (_temp_dir, bpm) = setup_real_bpm_environment(10);
285 let (page_id, frame_id) = {
286 let mut write_guard = bpm.new_page().unwrap();
287 write_guard.data_mut()[0] = 123;
288 write_guard.set_lsn(42);
289 write_guard.mark_dirty();
290 (write_guard.page_id(), write_guard.frame_id())
291 };
292
293 let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
294 assert!(meta.is_dirty);
295 assert_eq!(meta.lsn, 42);
296 assert_eq!(meta.pin_count, 0);
297
298 let read_guard = bpm.fetch_page_read(page_id).unwrap();
299 assert_eq!(read_guard.data()[0], 123);
300 assert!(read_guard.is_dirty());
301 assert_eq!(read_guard.lsn(), 42);
302 let snapshot = read_guard.meta_snapshot();
303 assert_eq!(snapshot.lsn, 42);
304 assert!(snapshot.is_dirty);
305 assert_eq!(snapshot.pin_count, 1);
306 drop(read_guard);
307
308 let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
309 assert!(meta.is_dirty);
310 assert_eq!(meta.lsn, 42);
311 assert_eq!(meta.pin_count, 0);
312 }
313
314 #[test]
315 fn test_write_guard_without_mutation_is_not_dirty() {
316 let (_temp_dir, bpm) = setup_real_bpm_environment(10);
317 let (page_id, frame_id) = {
318 let guard = bpm.new_page().unwrap();
319 (guard.page_id(), guard.frame_id())
320 };
321
322 {
323 let _write_guard = bpm.fetch_page_write(page_id).unwrap();
324 }
325
326 let read_guard = bpm.fetch_page_read(page_id).unwrap();
327 let snapshot = read_guard.meta_snapshot();
328 assert!(!snapshot.is_dirty);
329 assert_eq!(snapshot.lsn, 0);
330 assert_eq!(snapshot.pin_count, 1);
331 drop(read_guard);
332
333 let meta = bpm.buffer_pool().frame_meta(frame_id).clone();
334 assert!(!meta.is_dirty);
335 assert_eq!(meta.pin_count, 0);
336 }
337}