mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-05 01:04:22 +01:00
Use Rc instead of Arc and replace the concurrent LRU with single-threaded SIEVE. Fixes #23 Fixes #29
120 lines
3.2 KiB
Rust
120 lines
3.2 KiB
Rust
use crate::buffer_pool::BufferPool;
|
|
use crate::sqlite3_ondisk::BTreePage;
|
|
use crate::sqlite3_ondisk::{self, DatabaseHeader};
|
|
use crate::PageSource;
|
|
use log::trace;
|
|
use sieve_cache::SieveCache;
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
use std::sync::RwLock;
|
|
|
|
pub struct Page {
|
|
flags: AtomicUsize,
|
|
pub contents: RwLock<Option<BTreePage>>,
|
|
}
|
|
|
|
/// Page is up-to-date.
|
|
const PAGE_UPTODATE: usize = 0b001;
|
|
/// Page is locked for I/O to prevent concurrent access.
|
|
const PAGE_LOCKED: usize = 0b010;
|
|
/// Page had an I/O error.
|
|
const PAGE_ERROR: usize = 0b100;
|
|
|
|
impl Page {
|
|
pub fn new() -> Page {
|
|
Page {
|
|
flags: AtomicUsize::new(0),
|
|
contents: RwLock::new(None),
|
|
}
|
|
}
|
|
|
|
pub fn is_uptodate(&self) -> bool {
|
|
self.flags.load(Ordering::SeqCst) & PAGE_UPTODATE != 0
|
|
}
|
|
|
|
pub fn set_uptodate(&self) {
|
|
self.flags.fetch_or(PAGE_UPTODATE, Ordering::SeqCst);
|
|
}
|
|
|
|
pub fn clear_uptodate(&self) {
|
|
self.flags.fetch_and(!PAGE_UPTODATE, Ordering::SeqCst);
|
|
}
|
|
|
|
pub fn is_locked(&self) -> bool {
|
|
self.flags.load(Ordering::SeqCst) & PAGE_LOCKED != 0
|
|
}
|
|
|
|
pub fn set_locked(&self) {
|
|
self.flags.fetch_or(PAGE_LOCKED, Ordering::SeqCst);
|
|
}
|
|
|
|
pub fn clear_locked(&self) {
|
|
self.flags.fetch_and(!PAGE_LOCKED, Ordering::SeqCst);
|
|
}
|
|
|
|
pub fn is_error(&self) -> bool {
|
|
self.flags.load(Ordering::SeqCst) & PAGE_ERROR != 0
|
|
}
|
|
|
|
pub fn set_error(&self) {
|
|
self.flags.fetch_or(PAGE_ERROR, Ordering::SeqCst);
|
|
}
|
|
|
|
pub fn clear_error(&self) {
|
|
self.flags.fetch_and(!PAGE_ERROR, Ordering::SeqCst);
|
|
}
|
|
}
|
|
|
|
pub struct Pager {
|
|
page_source: PageSource,
|
|
page_cache: RefCell<SieveCache<usize, Rc<Page>>>,
|
|
buffer_pool: Rc<BufferPool>,
|
|
}
|
|
|
|
impl Pager {
|
|
pub fn begin_open(page_source: &PageSource) -> anyhow::Result<Rc<RefCell<DatabaseHeader>>> {
|
|
sqlite3_ondisk::begin_read_database_header(page_source)
|
|
}
|
|
|
|
pub fn finish_open(
|
|
db_header: Rc<RefCell<DatabaseHeader>>,
|
|
page_source: PageSource,
|
|
) -> anyhow::Result<Self> {
|
|
let db_header = db_header.borrow();
|
|
let page_size = db_header.page_size as usize;
|
|
let buffer_pool = Rc::new(BufferPool::new(page_size));
|
|
let page_cache = RefCell::new(SieveCache::new(10).unwrap());
|
|
Ok(Self {
|
|
page_source,
|
|
buffer_pool,
|
|
page_cache,
|
|
})
|
|
}
|
|
|
|
pub fn read_page(&self, page_idx: usize) -> anyhow::Result<Rc<Page>> {
|
|
trace!("read_page(page_idx = {})", page_idx);
|
|
let mut page_cache = self.page_cache.borrow_mut();
|
|
if let Some(page) = page_cache.get(&page_idx) {
|
|
return Ok(page.clone());
|
|
}
|
|
let page = Rc::new(Page::new());
|
|
page.set_locked();
|
|
sqlite3_ondisk::begin_read_btree_page(
|
|
&self.page_source,
|
|
self.buffer_pool.clone(),
|
|
page.clone(),
|
|
page_idx,
|
|
)
|
|
.unwrap();
|
|
page_cache.insert(page_idx, page.clone());
|
|
Ok(page)
|
|
/*
|
|
let handle = self.page_cache.get_or_try_init(page_idx, 1, |_idx| {
|
|
Ok::<Rc<Page>, anyhow::Error>(page)
|
|
})?;
|
|
Ok(handle.value().clone())
|
|
*/
|
|
}
|
|
}
|