Skip to content

Commit d9f9509

Browse files
authored
Share memory support (#76)
The lifetime semantics of returned mapped regions are odd: the regions are cached until all being released at once with `xShmUnmap`, but region 0 (the start of the file) might be fetched more than one. This makes a safe Rust interface nearly impossible, as sqlite wants to mutably alias whatever you return to it.
1 parent bf173b5 commit d9f9509

2 files changed

Lines changed: 121 additions & 6 deletions

File tree

src/flags.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,29 @@ impl From<i32> for LockLevel {
178178
}
179179
}
180180
}
181+
182+
#[derive(Copy, Clone, Debug)]
183+
pub enum ShmLockMode {
184+
LockShared,
185+
LockExclusive,
186+
UnlockShared,
187+
UnlockExclusive,
188+
}
189+
190+
impl TryFrom<i32> for ShmLockMode {
191+
type Error = i32;
192+
193+
fn try_from(flags: i32) -> Result<Self, Self::Error> {
194+
const LOCK_SHARED: i32 = vars::SQLITE_SHM_LOCK | vars::SQLITE_SHM_SHARED;
195+
const LOCK_EXCLUSIVE: i32 = vars::SQLITE_SHM_LOCK | vars::SQLITE_SHM_EXCLUSIVE;
196+
const UNLOCK_SHARED: i32 = vars::SQLITE_SHM_UNLOCK | vars::SQLITE_SHM_SHARED;
197+
const UNLOCK_EXCLUSIVE: i32 = vars::SQLITE_SHM_UNLOCK | vars::SQLITE_SHM_EXCLUSIVE;
198+
Ok(match flags {
199+
LOCK_SHARED => Self::LockShared,
200+
LOCK_EXCLUSIVE => Self::LockExclusive,
201+
UNLOCK_SHARED => Self::UnlockShared,
202+
UNLOCK_EXCLUSIVE => Self::UnlockExclusive,
203+
_ => return Err(vars::SQLITE_IOERR),
204+
})
205+
}
206+
}

src/vfs.rs

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::flags::{AccessFlags, LockLevel, OpenOpts};
1+
use crate::flags::{AccessFlags, LockLevel, OpenOpts, ShmLockMode};
22
use crate::logger::SqliteLogger;
33
use crate::vars::SQLITE_ERROR;
44
use crate::{ffi, vars};
@@ -11,7 +11,7 @@ use core::mem::{ManuallyDrop, size_of};
1111
use core::slice;
1212
use core::{
1313
ffi::{CStr, c_char, c_int, c_void},
14-
ptr::null_mut,
14+
ptr::{NonNull, null_mut},
1515
};
1616

1717
/// The minimim supported `SQLite` version.
@@ -186,6 +186,32 @@ pub trait Vfs: Send + Sync {
186186
fn device_characteristics(&self, handle: &mut Self::Handle) -> VfsResult<i32> {
187187
Ok(DEFAULT_DEVICE_CHARACTERISTICS)
188188
}
189+
190+
fn shm_map(
191+
&self,
192+
handle: &mut Self::Handle,
193+
region_idx: usize,
194+
region_size: usize,
195+
extend: bool,
196+
) -> VfsResult<Option<NonNull<u8>>> {
197+
Err(vars::SQLITE_READONLY_CANTINIT)
198+
}
199+
200+
fn shm_lock(
201+
&self,
202+
handle: &mut Self::Handle,
203+
offset: u32,
204+
count: u32,
205+
mode: ShmLockMode,
206+
) -> VfsResult<()> {
207+
Err(vars::SQLITE_IOERR)
208+
}
209+
210+
fn shm_barrier(&self, handle: &mut Self::Handle) {}
211+
212+
fn shm_unmap(&self, handle: &mut Self::Handle, delete: bool) -> VfsResult<()> {
213+
Err(vars::SQLITE_IOERR)
214+
}
189215
}
190216

191217
#[derive(Clone)]
@@ -300,10 +326,10 @@ fn register_inner<T: Vfs>(
300326
xFileControl: Some(x_file_control::<T>),
301327
xSectorSize: Some(x_sector_size::<T>),
302328
xDeviceCharacteristics: Some(x_device_characteristics::<T>),
303-
xShmMap: None,
304-
xShmLock: None,
305-
xShmBarrier: None,
306-
xShmUnmap: None,
329+
xShmMap: Some(x_shm_map::<T>),
330+
xShmLock: Some(x_shm_lock::<T>),
331+
xShmBarrier: Some(x_shm_barrier::<T>),
332+
xShmUnmap: Some(x_shm_unmap::<T>),
307333
xFetch: None,
308334
xUnfetch: None,
309335
};
@@ -657,6 +683,69 @@ unsafe extern "C" fn x_device_characteristics<T: Vfs>(p_file: *mut ffi::sqlite3_
657683
})
658684
}
659685

686+
unsafe extern "C" fn x_shm_map<T: Vfs>(
687+
p_file: *mut ffi::sqlite3_file,
688+
pg: c_int,
689+
pgsz: c_int,
690+
extend: c_int,
691+
p_page: *mut *mut c_void,
692+
) -> c_int {
693+
fallible(|| {
694+
let file = unwrap_file!(p_file, T)?;
695+
let vfs = unwrap_vfs!(file.vfs, T)?;
696+
if let Some(region) = vfs.shm_map(
697+
&mut file.handle,
698+
pg.try_into().map_err(|_| vars::SQLITE_IOERR)?,
699+
pgsz.try_into().map_err(|_| vars::SQLITE_IOERR)?,
700+
extend != 0,
701+
)? {
702+
unsafe { *p_page = region.as_ptr() as *mut c_void }
703+
} else {
704+
unsafe { *p_page = null_mut() }
705+
}
706+
Ok(vars::SQLITE_OK)
707+
})
708+
}
709+
710+
unsafe extern "C" fn x_shm_lock<T: Vfs>(
711+
p_file: *mut ffi::sqlite3_file,
712+
offset: c_int,
713+
n: c_int,
714+
flags: c_int,
715+
) -> c_int {
716+
fallible(|| {
717+
let file = unwrap_file!(p_file, T)?;
718+
let vfs = unwrap_vfs!(file.vfs, T)?;
719+
vfs.shm_lock(
720+
&mut file.handle,
721+
offset.try_into().map_err(|_| vars::SQLITE_IOERR)?,
722+
n.try_into().map_err(|_| vars::SQLITE_IOERR)?,
723+
ShmLockMode::try_from(flags)?,
724+
)?;
725+
Ok(vars::SQLITE_OK)
726+
})
727+
}
728+
729+
unsafe extern "C" fn x_shm_barrier<T: Vfs>(p_file: *mut ffi::sqlite3_file) {
730+
if let Ok(file) = unwrap_file!(p_file, T) {
731+
if let Ok(vfs) = unwrap_vfs!(file.vfs, T) {
732+
vfs.shm_barrier(&mut file.handle)
733+
}
734+
}
735+
}
736+
737+
unsafe extern "C" fn x_shm_unmap<T: Vfs>(
738+
p_file: *mut ffi::sqlite3_file,
739+
delete_flag: c_int,
740+
) -> c_int {
741+
fallible(|| {
742+
let file = unwrap_file!(p_file, T)?;
743+
let vfs = unwrap_vfs!(file.vfs, T)?;
744+
vfs.shm_unmap(&mut file.handle, delete_flag != 0)?;
745+
Ok(vars::SQLITE_OK)
746+
})
747+
}
748+
660749
// the following functions are wrappers around the base vfs functions
661750

662751
unsafe extern "C" fn x_dlopen<T: Vfs>(

0 commit comments

Comments
 (0)