1use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
2
3use crate::latch::RwLatch;
4use crate::page_id::{PAGE_SIZE, PageId};
5
6pub struct Frame {
15 data: *mut u8,
17 page_id: AtomicU64,
19 pin_count: AtomicU32,
21 dirty: AtomicBool,
23 recently_used: AtomicBool,
25 latch: RwLatch,
27}
28
29unsafe impl Send for Frame {}
31unsafe impl Sync for Frame {}
32
33impl Frame {
34 pub fn new() -> Self {
36 let layout =
37 std::alloc::Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).expect("invalid page layout");
38 let data = unsafe { std::alloc::alloc_zeroed(layout) };
40 if data.is_null() {
41 std::alloc::handle_alloc_error(layout);
42 }
43 Self {
44 data,
45 page_id: AtomicU64::new(PageId::INVALID.to_u64()),
46 pin_count: AtomicU32::new(0),
47 dirty: AtomicBool::new(false),
48 recently_used: AtomicBool::new(false),
49 latch: RwLatch::new(),
50 }
51 }
52
53 pub unsafe fn data(&self) -> &[u8] {
59 unsafe { std::slice::from_raw_parts(self.data, PAGE_SIZE) }
61 }
62
63 #[allow(clippy::mut_from_ref)]
71 pub unsafe fn data_mut(&self) -> &mut [u8] {
72 unsafe { std::slice::from_raw_parts_mut(self.data, PAGE_SIZE) }
74 }
75
76 pub fn page_id(&self) -> PageId {
78 PageId::from_u64(self.page_id.load(Ordering::Acquire))
79 }
80
81 pub fn set_page_id(&self, pid: PageId) {
83 self.page_id.store(pid.to_u64(), Ordering::Release);
84 }
85
86 pub fn pin_count(&self) -> u32 {
88 self.pin_count.load(Ordering::Acquire)
89 }
90
91 pub fn pin(&self) -> u32 {
93 self.pin_count.fetch_add(1, Ordering::AcqRel) + 1
94 }
95
96 pub fn unpin(&self) -> u32 {
98 let prev = self.pin_count.fetch_sub(1, Ordering::AcqRel);
99 debug_assert!(prev > 0, "unpin called on unpinned frame");
100 prev - 1
101 }
102
103 pub fn is_pinned(&self) -> bool {
105 self.pin_count() > 0
106 }
107
108 pub fn is_dirty(&self) -> bool {
110 self.dirty.load(Ordering::Acquire)
111 }
112
113 pub fn set_dirty(&self) {
115 self.dirty.store(true, Ordering::Release);
116 }
117
118 pub fn clear_dirty(&self) {
120 self.dirty.store(false, Ordering::Release);
121 }
122
123 pub fn is_recently_used(&self) -> bool {
125 self.recently_used.load(Ordering::Relaxed)
126 }
127
128 pub fn set_recently_used(&self) {
130 self.recently_used.store(true, Ordering::Relaxed);
131 }
132
133 pub fn clear_recently_used(&self) {
135 self.recently_used.store(false, Ordering::Relaxed);
136 }
137
138 pub fn latch(&self) -> &RwLatch {
140 &self.latch
141 }
142
143 pub fn reset(&self) {
145 self.page_id
146 .store(PageId::INVALID.to_u64(), Ordering::Release);
147 self.dirty.store(false, Ordering::Release);
148 self.recently_used.store(false, Ordering::Relaxed);
149 debug_assert_eq!(self.pin_count(), 0);
151 }
152
153 pub fn has_valid_page(&self) -> bool {
155 self.page_id().is_valid()
156 }
157}
158
159impl Drop for Frame {
160 fn drop(&mut self) {
161 let layout =
162 std::alloc::Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).expect("invalid page layout");
163 unsafe {
165 std::alloc::dealloc(self.data, layout);
166 }
167 }
168}
169
170impl Default for Frame {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use crate::page_id::FileId;
180
181 #[test]
182 fn new_frame() {
183 let frame = Frame::new();
184 assert!(!frame.has_valid_page());
185 assert_eq!(frame.pin_count(), 0);
186 assert!(!frame.is_dirty());
187 assert!(!frame.is_recently_used());
188 assert!(!frame.is_pinned());
189 }
190
191 #[test]
192 fn data_is_zeroed() {
193 let frame = Frame::new();
194 let data = unsafe { frame.data() };
196 assert!(data.iter().all(|&b| b == 0));
197 assert_eq!(data.len(), PAGE_SIZE);
198 }
199
200 #[test]
201 fn write_and_read_data() {
202 let frame = Frame::new();
203 unsafe {
205 let data = frame.data_mut();
206 data[0] = 0xDE;
207 data[1] = 0xAD;
208 data[PAGE_SIZE - 1] = 0xFF;
209 }
210 let data = unsafe { frame.data() };
211 assert_eq!(data[0], 0xDE);
212 assert_eq!(data[1], 0xAD);
213 assert_eq!(data[PAGE_SIZE - 1], 0xFF);
214 }
215
216 #[test]
217 fn set_page_id() {
218 let frame = Frame::new();
219 let pid = PageId::new(FileId(1), 42);
220 frame.set_page_id(pid);
221 assert_eq!(frame.page_id(), pid);
222 assert!(frame.has_valid_page());
223 }
224
225 #[test]
226 fn pin_and_unpin() {
227 let frame = Frame::new();
228 assert_eq!(frame.pin(), 1);
229 assert_eq!(frame.pin(), 2);
230 assert!(frame.is_pinned());
231 assert_eq!(frame.unpin(), 1);
232 assert_eq!(frame.unpin(), 0);
233 assert!(!frame.is_pinned());
234 }
235
236 #[test]
237 fn dirty_flag() {
238 let frame = Frame::new();
239 assert!(!frame.is_dirty());
240 frame.set_dirty();
241 assert!(frame.is_dirty());
242 frame.clear_dirty();
243 assert!(!frame.is_dirty());
244 }
245
246 #[test]
247 fn recently_used_flag() {
248 let frame = Frame::new();
249 assert!(!frame.is_recently_used());
250 frame.set_recently_used();
251 assert!(frame.is_recently_used());
252 frame.clear_recently_used();
253 assert!(!frame.is_recently_used());
254 }
255
256 #[test]
257 fn reset() {
258 let frame = Frame::new();
259 frame.set_page_id(PageId::new(FileId(1), 0));
260 frame.set_dirty();
261 frame.set_recently_used();
262 frame.reset();
263 assert!(!frame.has_valid_page());
264 assert!(!frame.is_dirty());
265 assert!(!frame.is_recently_used());
266 }
267
268 #[test]
269 fn latch_access() {
270 let frame = Frame::new();
271 frame.latch().lock_shared();
272 assert!(!frame.latch().is_unlocked());
273 frame.latch().unlock_shared();
274 assert!(frame.latch().is_unlocked());
275 }
276}