Skip to content

Commit 2fc5119

Browse files
committed
WIP: use std file_lock
1 parent 49b5ad0 commit 2fc5119

File tree

8 files changed

+130
-207
lines changed

8 files changed

+130
-207
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343
- name: Install Rust
4444
if: steps.rust-cache.outputs.cache-hit != 'true'
4545
run: |
46+
rustup default nightly-2025-05-04
4647
rustup component add rustfmt
4748
rustup component add clippy
4849

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pyo3-build-config = { version = "0.24.1", optional = true }
2121
log = { version = "0.4.17", optional = true }
2222
pyo3 = { version = "0.24.1", features=["extension-module", "abi3-py37"], optional = true }
2323

24-
[target.'cfg(unix)'.dependencies]
24+
[target.'cfg(target_os = "macos")'.dependencies]
2525
libc = "0.2.104"
2626

2727
# Common test/bench dependencies

rust-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.85
1+
nightly-2025-05-04

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
)]
2525
// TODO remove this once wasi no longer requires nightly
2626
#![cfg_attr(target_os = "wasi", feature(wasi_ext))]
27+
#![feature(file_lock)]
2728

2829
//! # redb
2930
//!

src/tree_store/page_store/file_backend/mod.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
#[cfg(any(unix, target_os = "wasi"))]
2-
mod unix;
3-
#[cfg(any(unix, target_os = "wasi"))]
4-
pub use unix::FileBackend;
5-
6-
#[cfg(windows)]
7-
mod windows;
8-
#[cfg(windows)]
9-
pub use windows::FileBackend;
1+
#[cfg(any(windows, unix, target_os = "wasi"))]
2+
mod optimized;
3+
#[cfg(any(windows, unix, target_os = "wasi"))]
4+
pub use optimized::FileBackend;
105

116
#[cfg(not(any(windows, unix, target_os = "wasi")))]
127
mod fallback;
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use crate::{DatabaseError, Result, StorageBackend};
2+
use std::fs::{File, TryLockError};
3+
use std::io;
4+
5+
#[cfg(feature = "logging")]
6+
use log::warn;
7+
8+
#[cfg(unix)]
9+
use std::os::unix::fs::FileExt;
10+
11+
#[cfg(windows)]
12+
use std::os::windows::fs::FileExt;
13+
14+
#[cfg(target_os = "wasi")]
15+
use std::os::wasi::fs::FileExt;
16+
17+
#[cfg(target_os = "macos")]
18+
use std::os::unix::io::AsRawFd;
19+
20+
/// Stores a database as a file on-disk.
21+
#[derive(Debug)]
22+
pub struct FileBackend {
23+
lock_supported: bool,
24+
file: File,
25+
}
26+
27+
impl FileBackend {
28+
/// Creates a new backend which stores data to the given file.
29+
pub fn new(file: File) -> Result<Self, DatabaseError> {
30+
match file.try_lock() {
31+
Ok(()) => Ok(Self {
32+
file,
33+
lock_supported: true,
34+
}),
35+
Err(TryLockError::WouldBlock) => Err(DatabaseError::DatabaseAlreadyOpen),
36+
Err(TryLockError::Error(err)) if err.kind() == io::ErrorKind::Unsupported => {
37+
#[cfg(feature = "logging")]
38+
warn!(
39+
"File locks not supported on this platform. You must ensure that only a single process opens the database file, at a time"
40+
);
41+
42+
Ok(Self {
43+
file,
44+
lock_supported: false,
45+
})
46+
}
47+
Err(TryLockError::Error(err)) => Err(err.into()),
48+
}
49+
}
50+
}
51+
52+
impl StorageBackend for FileBackend {
53+
fn len(&self) -> Result<u64, io::Error> {
54+
Ok(self.file.metadata()?.len())
55+
}
56+
57+
#[cfg(any(unix, target_os = "wasi"))]
58+
fn read(&self, offset: u64, len: usize) -> Result<Vec<u8>, io::Error> {
59+
let mut buffer = vec![0; len];
60+
self.file.read_exact_at(&mut buffer, offset)?;
61+
Ok(buffer)
62+
}
63+
64+
#[cfg(windows)]
65+
fn read(&self, mut offset: u64, len: usize) -> Result<Vec<u8>, io::Error> {
66+
let mut buffer = vec![0; len];
67+
let mut data_offset = 0;
68+
while data_offset < buffer.len() {
69+
let read = self.file.seek_read(&mut buffer[data_offset..], offset)?;
70+
offset += read as u64;
71+
data_offset += read;
72+
}
73+
Ok(buffer)
74+
}
75+
76+
fn set_len(&self, len: u64) -> Result<(), io::Error> {
77+
self.file.set_len(len)
78+
}
79+
80+
#[cfg(not(target_os = "macos"))]
81+
fn sync_data(&self, _: bool) -> Result<(), io::Error> {
82+
self.file.sync_data()
83+
}
84+
85+
#[cfg(target_os = "macos")]
86+
fn sync_data(&self, eventual: bool) -> Result<(), io::Error> {
87+
if eventual {
88+
let code = unsafe { libc::fcntl(self.file.as_raw_fd(), libc::F_BARRIERFSYNC) };
89+
if code == -1 {
90+
Err(io::Error::last_os_error())
91+
} else {
92+
Ok(())
93+
}
94+
} else {
95+
self.file.sync_data()
96+
}
97+
}
98+
99+
#[cfg(any(unix, target_os = "wasi"))]
100+
fn write(&self, offset: u64, data: &[u8]) -> Result<(), io::Error> {
101+
self.file.write_all_at(data, offset)
102+
}
103+
104+
#[cfg(windows)]
105+
fn write(&self, mut offset: u64, data: &[u8]) -> Result<(), io::Error> {
106+
let mut data_offset = 0;
107+
while data_offset < data.len() {
108+
let written = self.file.seek_write(&data[data_offset..], offset)?;
109+
offset += written as u64;
110+
data_offset += written;
111+
}
112+
Ok(())
113+
}
114+
}
115+
116+
impl Drop for FileBackend {
117+
fn drop(&mut self) {
118+
if self.lock_supported {
119+
let _ = self.file.unlock();
120+
}
121+
}
122+
}

src/tree_store/page_store/file_backend/unix.rs

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/tree_store/page_store/file_backend/windows.rs

Lines changed: 0 additions & 101 deletions
This file was deleted.

0 commit comments

Comments
 (0)