quill_sql/buffer/
page.rs

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, // 使用原子操作,避免锁竞争
21    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), // set by caller with proper ordering
31            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    // Public getter for page_id
47    pub fn page_id(&self) -> PageId {
48        self.page_id
49    }
50
51    // Public getter for data
52    pub fn data(&self) -> &[u8; PAGE_SIZE] {
53        &self.data
54    }
55
56    // Public method to reset the page memory, used in delete_page.
57    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    // 原子操作:增加pin_count(Acquire 以发布可见性给随后读)
66    pub fn pin(&self) -> u32 {
67        self.pin_count.fetch_add(1, Ordering::AcqRel) + 1
68    }
69
70    // 原子操作:减少pin_count(Release 以与获取/驱逐建立次序)
71    pub fn unpin(&self) -> u32 {
72        self.pin_count.fetch_sub(1, Ordering::AcqRel)
73    }
74
75    // 原子操作:获取当前pin_count(Acquire 保守读取)
76    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/// 一个只读的页面保护器。
90/// 它的存在就意味着持有了页面的读锁和 pin。
91/// 实现了 Deref,可以像 &Page 一样直接使用。
92#[derive(Debug)]
93pub struct ReadPageGuard {
94    bpm: Arc<BufferPoolManager>,
95    // 这个 Arc 必须持有,以确保 guard 指向的数据是有效的。
96    // 我们用 _ 前缀表示它只是为了维持生命周期。
97    _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
108// 为ReadPageGuard添加pin_count的便捷访问
109impl 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        // Unpin under latch (safe because evictor requires write lock to evict)
118        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/// 一个可写的页面保护器。
131/// 它的存在就意味着持有了页面的写锁和 pin。
132/// 实现了 Deref 和 DerefMut,可以像 &mut Page 一样直接使用。
133#[derive(Debug)]
134pub struct WritePageGuard {
135    bpm: Arc<BufferPoolManager>,
136    _page: Arc<RwLock<Page>>,
137    guard: ManuallyDrop<RwLockWriteGuard<'static, Page>>,
138    /// Page LSN captured when we first dirtied in this guard's lifetime; used for recLSN.
139    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        // 任何可变访问都应该将页面标记为脏页。
152        self.guard.is_dirty = true;
153        &mut self.guard
154    }
155}
156
157// 为WritePageGuard添加pin_count的便捷访问
158impl 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        // Unpin under latch (safe with our eviction protocol)
179        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
202// 工厂函数,隐藏 `unsafe` 实现细节
203pub(crate) fn new_read_guard(
204    bpm: Arc<BufferPoolManager>,
205    page: Arc<RwLock<Page>>,
206) -> ReadPageGuard {
207    let guard = page.read();
208    // SAFETY: 这是安全的核心。我们将 guard 的生命周期从 page 的借用中分离出来。
209    // 这是可行的,因为 ReadPageGuard 结构体本身持有一个 page 的 Arc 引用,
210    // 保证了在 guard 的生命周期内,底层的 RwLock 不会被销毁。
211    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    // SAFETY: 同上,WritePageGuard 持有 page 的 Arc 引用,保证了安全性。
225    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    // 引入真实的 BufferPoolManager 及其依赖
240    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    // 引入需要被测试的 Page 结构体
245    use super::{Page, INVALID_PAGE_ID};
246
247    /// 辅助函数,用于为 page 模块的测试设置一个真实的环境。
248    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        // 确认在创建后,pin_count 为 0,页面在 replacer 中
281        assert_eq!(bpm.replacer.read().size(), 1);
282
283        {
284            let read_guard = bpm.fetch_page_read(page_id).unwrap();
285
286            // 1. 测试 Deref - 可以直接访问页面字段
287            assert_eq!(read_guard.page_id(), page_id);
288            assert_eq!(read_guard.pin_count(), 1);
289
290            // 页面被 pin 住,应该从 replacer 中移除
291            assert_eq!(bpm.replacer.read().size(), 0);
292
293            // read_guard 在此 drop
294        }
295
296        // 2. 测试 Drop - 确认页面被 unpin,并回到 replacer
297        assert_eq!(bpm.replacer.read().size(), 1);
298
299        // 验证 pin count 已经降回 0
300        let final_check_guard = bpm.fetch_page_read(page_id).unwrap();
301        // pin_count 再次变为 1,因为我们又 fetch 了一次
302        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            // 1. 测试 DerefMut - 写入数据
315            write_guard.data[0] = 123;
316
317            // 2. 确认写入操作将 is_dirty 标记为 true
318            assert!(write_guard.is_dirty);
319
320            // 页面被 pin 住,replacer 应为空
321            assert_eq!(bpm.replacer.read().size(), 0);
322
323            // write_guard 在此 drop
324        }
325
326        // 3. 测试 Drop - 页面被 unpin,回到 replacer
327        assert_eq!(bpm.replacer.read().size(), 1);
328
329        // 4. 重新获取页面,验证数据和脏位
330        let read_guard = bpm.fetch_page_read(page_id).unwrap();
331        assert_eq!(read_guard.data()[0], 123);
332        assert!(read_guard.is_dirty); // 脏位应该被保留
333    }
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(); // 创建并立即 unpin
339
340        {
341            // 获取一个 WriteGuard 但不修改它
342            let write_guard = bpm.fetch_page_write(page_id).unwrap();
343            // 只通过 Deref 读取
344            assert_eq!(write_guard.page_id(), page_id);
345            // write_guard 在此 drop
346        }
347
348        // 重新获取页面,验证它没有被标记为脏页
349        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(); // 直接访问内部 pool 以便测试锁
357
358        // 为了测试,我们手动在 frame 0 上创建一个页面
359        let page_id = 100;
360        bpm.page_table.insert(page_id, 0);
361        *page_arc.write() = Page::new(page_id);
362
363        // 1. 获取一个写保护器
364        let mut write_guard = bpm.fetch_page_write(page_id).unwrap();
365
366        // 2. 尝试在持有写锁的同时获取读锁会失败
367        assert!(page_arc.try_read().is_none());
368        assert!(page_arc.try_write().is_none());
369
370        // 3. 修改数据
371        write_guard.data[0] = 123;
372
373        // 4. 释放写保护器
374        drop(write_guard);
375
376        // 5. 现在应该可以成功获取读锁了
377        let read_guard = bpm.fetch_page_read(page_id).unwrap();
378        assert!(page_arc.try_write().is_none()); // 但仍然不能获取写锁
379        assert_eq!(read_guard.data[0], 123);
380    }
381}