diff --git a/README.adoc b/README.adoc index c3acbab..4c1140e 100644 --- a/README.adoc +++ b/README.adoc @@ -24,7 +24,13 @@ Efficiently removing data, and creating read snapshots requires hole punching, a == Supported systems -macOS, Linux and Windows are supported. FreeBSD doesn't work yet, unfortunately file cloning and open file description locking is missing, but there are fallbacks that can be used. Solaris requires a small amount of work to complete the implementation. On systems where block cloning is not supported (ext4 on Linux, and NTFS on Windows are notable examples), the implementation falls back to file region locking (except for FreeBSD which will have to fall back to whole-file locking). +macOS, Linux and Windows. + +FreeBSD 14+. Unfortunately file cloning and open file description locking are missing. The implementation falls back on flock(2) and has to separate write and read files. + +Solaris requires a small amount of work to complete the implementation. + +On filesystems where block cloning is not supported (ext4 on Linux, and NTFS on Windows are notable examples), the implementation falls back to file region locking unless that is also not available (FreeBSD). == anacrolix/squirrel diff --git a/README.md b/README.md index 3d07e43..c70d03f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,13 @@ Efficiently removing data, and creating read snapshots requires hole punching, a ## Supported systems -macOS, Linux and Windows are supported. FreeBSD doesn’t work yet, unfortunately file cloning and open file description locking is missing, but there are fallbacks that can be used. Solaris requires a small amount of work to complete the implementation. On systems where block cloning is not supported (ext4 on Linux, and NTFS on Windows are notable examples), the implementation falls back to file region locking (except for FreeBSD which will have to fall back to whole-file locking). +macOS, Linux and Windows. + +FreeBSD 14+. Unfortunately file cloning and open file description locking are missing. The implementation falls back on flock(2) and has to separate write and read files. + +Solaris requires a small amount of work to complete the implementation. + +On filesystems where block cloning is not supported (ext4 on Linux, and NTFS on Windows are notable examples), the implementation falls back to file region locking unless that is also not available (FreeBSD). ## anacrolix/squirrel diff --git a/src/env.rs b/src/env.rs index 2f915ae..3000a13 100644 --- a/src/env.rs +++ b/src/env.rs @@ -8,10 +8,14 @@ pub(crate) fn emulate_freebsd() -> bool { use once_cell::sync::OnceCell; static CELL: OnceCell = OnceCell::new(); *CELL.get_or_init(|| { - !matches!( + let emulate = !matches!( std::env::var("POSSUM_EMULATE_FREEBSD"), Err(std::env::VarError::NotPresent) - ) + ); + if emulate { + super::error!("emulating freebsd"); + } + emulate }) } } diff --git a/src/exclusive_file.rs b/src/exclusive_file.rs index 01d090c..d186a34 100644 --- a/src/exclusive_file.rs +++ b/src/exclusive_file.rs @@ -8,6 +8,8 @@ use crate::FileId; #[derive(Debug)] enum LockLevel { + // On systems that don't implement flock, downgrades never occur. + #[allow(dead_code)] Shared, Exclusive, } diff --git a/src/handle.rs b/src/handle.rs index 9f38e6b..8dcf55f 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -23,7 +23,7 @@ pub struct Handle { pub(crate) instance_limits: Limits, deleted_values: Option, _value_puncher: Option>, - pub(crate) value_puncher_done: Arc>>, + value_puncher_done: ValuePuncherDone, } /// 4 bytes stored in the database header https://sqlite.org/fileformat2.html#database_header. @@ -108,7 +108,7 @@ impl Handle { Self::init_sqlite_conn(&mut conn)?; let (deleted_values, receiver) = std::sync::mpsc::sync_channel(10); let (value_puncher_done_sender, value_puncher_done) = std::sync::mpsc::sync_channel(0); - let value_puncher_done = Arc::new(Mutex::new(value_puncher_done)); + let value_puncher_done = ValuePuncherDone(Arc::new(Mutex::new(value_puncher_done))); let handle = Self { conn: Mutex::new(conn), exclusive_files: Default::default(), @@ -350,7 +350,6 @@ impl Handle { file_id, file_offset, length, - .. } = &v; let value_length = length; let msg = format!( @@ -390,6 +389,11 @@ impl Handle { } } } + + /// Returns something that can be used to test if the value puncher routine for this Handle has returned. + pub fn get_value_puncher_done(&self) -> ValuePuncherDone { + ValuePuncherDone(Arc::clone(&self.value_puncher_done.0)) + } } use item::Item; @@ -407,3 +411,15 @@ impl Drop for Handle { // } } } + +#[derive(Debug)] +pub struct ValuePuncherDone(Arc>>); + +impl ValuePuncherDone { + pub fn wait(&self) { + assert!(matches!( + self.0.lock().unwrap().recv(), + Err(std::sync::mpsc::RecvError) + )) + } +} diff --git a/src/lib.rs b/src/lib.rs index 722fe54..af16b05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -816,6 +816,7 @@ impl<'a> Reader<'a> { if flocking() { // Possibly we want to block if we're flocking. assert!(file.flock(LockShared)?); + return Ok(()); } for extent in read_extents { assert!(file.lock_segment(LockSharedNonblock, Some(extent.len), extent.offset)?); diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 6e13525..926aef0 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -15,7 +15,7 @@ pub use flock::*; pub(crate) use pathconf::*; pub use punchfile::*; -use crate::env::flocking; +use crate::env::{emulate_freebsd, flocking}; cfg_if! { if #[cfg(windows)] { @@ -88,7 +88,7 @@ impl FileSystemFlags for UnixFilesystemFlags { // AFAIK there's no way to check if a filesystem supports block cloning on non-Windows // platforms, and even then it depends on where you're copying to/from, sometimes even on // the same filesystem. - if flocking() { + if emulate_freebsd() { Some(false) } else { None diff --git a/src/tests.rs b/src/tests.rs index 6d0b821..7f3d3db 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -74,10 +74,10 @@ fn test_replace_keys() -> Result<()> { ); let dir = handle.dir.clone(); - let values_punched = Arc::clone(&handle.value_puncher_done); + let values_punched = handle.get_value_puncher_done(); drop(handle); // Wait for it to recv, which should be a disconnect when the value_puncher hangs up. - values_punched.lock().unwrap().recv(); + values_punched.wait(); let entries = dir.walk_dir()?; let values_files: Vec<_> = entries