Skip to content

Commit 889b11a

Browse files
committed
feat: Mmap page manager only flushes dirty pages
1 parent 7f31df8 commit 889b11a

File tree

1 file changed

+37
-5
lines changed

1 file changed

+37
-5
lines changed

src/page/manager/mmap.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ use crate::{
55
use memmap2::{Advice, MmapOptions, MmapRaw};
66
use parking_lot::Mutex;
77
use std::{
8-
fs::File,
9-
io,
10-
path::Path,
11-
sync::atomic::{AtomicU32, AtomicU64, Ordering},
8+
collections::HashSet, fs::File, io, ops::Range, path::Path, sync::atomic::{AtomicU32, AtomicU64, Ordering}
129
};
1310

1411
// Manages pages in a memory mapped file.
@@ -18,6 +15,10 @@ pub struct PageManager {
1815
file: Mutex<File>,
1916
file_len: AtomicU64,
2017
page_count: AtomicU32,
18+
// A set of reallocated dirty pages which need to be flushed to disk.
19+
dirty_set: Mutex<HashSet<usize>>,
20+
// A range of newly allocated pages at the end of the file which need to be flushed to disk.
21+
dirty_range: Mutex<Option<Range<usize>>>,
2122
}
2223

2324
impl PageManager {
@@ -101,6 +102,8 @@ impl PageManager {
101102
file: Mutex::new(file),
102103
file_len: AtomicU64::new(file_len),
103104
page_count: AtomicU32::new(opts.page_count),
105+
dirty_set: Mutex::new(HashSet::new()),
106+
dirty_range: Mutex::new(None),
104107
})
105108
}
106109

@@ -213,6 +216,8 @@ impl PageManager {
213216
// SAFETY: We have checked that the page fits inside the memory map.
214217
let data = unsafe { self.mmap.as_mut_ptr().byte_add(offset).cast() };
215218

219+
self.add_dirty(page_id);
220+
216221
// TODO: This is actually unsafe, as it's possible to call `get()` arbitrary times before
217222
// calling this function (this will be fixed in a future commit).
218223
unsafe { PageMut::from_ptr(page_id, snapshot_id, data) }
@@ -234,6 +239,8 @@ impl PageManager {
234239
// SAFETY: We have checked that the page fits inside the memory map.
235240
let data = unsafe { self.mmap.as_mut_ptr().byte_add(offset).cast() };
236241

242+
self.extend_dirty_range(page_id);
243+
237244
// SAFETY:
238245
// - This is a newly created page at the end of the file, so we're guaranteed to have
239246
// exclusive access to it. Even if another thread was calling `allocate()` at the same
@@ -243,10 +250,35 @@ impl PageManager {
243250
unsafe { PageMut::acquire_unchecked(page_id, snapshot_id, data) }
244251
}
245252

253+
#[inline]
254+
fn add_dirty(&self, page_id: PageId) {
255+
self.dirty_set.lock().insert(page_id.as_offset());
256+
}
257+
258+
#[inline]
259+
fn extend_dirty_range(&self, page_id: PageId) {
260+
let mut dirty_range = self.dirty_range.lock();
261+
match dirty_range.as_mut() {
262+
Some(range) => {
263+
*dirty_range = Some(range.start..page_id.as_offset() + Page::SIZE);
264+
}
265+
None => {
266+
*dirty_range = Some(page_id.as_offset()..page_id.as_offset() + Page::SIZE);
267+
}
268+
}
269+
}
270+
246271
/// Syncs pages to the backing file.
247272
pub fn sync(&self) -> io::Result<()> {
248273
if cfg!(not(miri)) {
249-
self.mmap.flush()
274+
let mut dirty_set = self.dirty_set.lock();
275+
for offset in dirty_set.drain() {
276+
self.mmap.flush_range(offset, Page::SIZE)?;
277+
}
278+
if let Some(range) = self.dirty_range.lock().as_mut().take() {
279+
self.mmap.flush_range(range.start, range.end - range.start)?;
280+
}
281+
Ok(())
250282
} else {
251283
Ok(())
252284
}

0 commit comments

Comments
 (0)