@@ -5,10 +5,7 @@ use crate::{
55use  memmap2:: { Advice ,  MmapOptions ,  MmapRaw } ; 
66use  parking_lot:: Mutex ; 
77use  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
2324impl  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