diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index 577b47f..6b93e37 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -8,6 +8,8 @@ jobs: clippy_check_ubuntu: name: Clippy runs-on: ubuntu-latest + permissions: + checks: write steps: - uses: actions/checkout@v1 - run: rustup component add clippy diff --git a/benches/transaction.rs b/benches/transaction.rs index ddc4b60..c95f9fd 100644 --- a/benches/transaction.rs +++ b/benches/transaction.rs @@ -74,7 +74,7 @@ fn bench_put_rand(b: &mut Bencher) { b.iter(|| { let mut txn = env.begin_rw_txn(None).unwrap(); - for &(ref key, ref data) in items.iter() { + for (key, data) in items.iter() { txn.put(db, key, data, WriteFlags::empty()).unwrap(); } txn.abort(); @@ -106,7 +106,7 @@ fn bench_put_rand_raw(b: &mut Bencher) { mdb_txn_begin(env, ptr::null_mut(), 0, &mut txn); let mut i: ::libc::c_int = 0; - for &(ref key, ref data) in items.iter() { + for (key, data) in items.iter() { key_val.mv_size = key.len() as size_t; key_val.mv_data = key.as_bytes().as_ptr() as *mut _; data_val.mv_size = data.len() as size_t; diff --git a/lmdb-sys/build.rs b/lmdb-sys/build.rs index eafaba3..9d3b693 100644 --- a/lmdb-sys/build.rs +++ b/lmdb-sys/build.rs @@ -84,4 +84,11 @@ fn main() { builder.compile("liblmdb.a") } + + // Fix linker errors: + // "unresolved external symbol __imp_InitializeSecurityDescriptor referenced in function mdb_env_setup_locks", + // "unresolved external symbol __imp_SetSecurityDescriptorDacl referenced in function mdb_env_setup_locks". + if cfg!(windows) { + println!("cargo:rustc-link-lib=advapi32"); + } } diff --git a/lmdb-sys/tests/simple.rs b/lmdb-sys/tests/simple.rs index 6682f29..ac555d5 100644 --- a/lmdb-sys/tests/simple.rs +++ b/lmdb-sys/tests/simple.rs @@ -17,7 +17,7 @@ macro_rules! E { macro_rules! str { ($expr:expr) => { - CString::new($expr).unwrap().as_ptr() + CString::new($expr).unwrap() }; } @@ -57,16 +57,23 @@ fn test_simple(env_path: &str) { mv_data: ptr::null_mut(), }; let mut txn: *mut MDB_txn = ptr::null_mut(); - let sval = str!("foo") as *mut c_void; - let dval = str!("bar") as *mut c_void; + let s = str!("foo"); + let sval = s.as_ptr() as *mut c_void; + let d = str!("bar"); + let dval = d.as_ptr() as *mut c_void; unsafe { E!(mdb_env_create(&mut env)); E!(mdb_env_set_maxdbs(env, 2)); - E!(mdb_env_open(env, str!(env_path), 0, 664)); + E!(mdb_env_open(env, str!(env_path).as_ptr(), 0, 664)); E!(mdb_txn_begin(env, ptr::null_mut(), 0, &mut txn)); - E!(mdb_dbi_open(txn, str!("subdb"), MDB_CREATE, &mut dbi)); + E!(mdb_dbi_open( + txn, + str!("subdb").as_ptr(), + MDB_CREATE, + &mut dbi + )); E!(mdb_txn_commit(txn)); key.mv_size = 3; diff --git a/src/cursor.rs b/src/cursor.rs index 5a3698f..90b644d 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -415,7 +415,7 @@ mod test { { let mut txn = env.begin_rw_txn(None).unwrap(); - for &(ref key, ref data) in &items { + for (key, data) in &items { txn.put(db, key, data, WriteFlags::empty()).unwrap(); } txn.commit().unwrap(); @@ -530,7 +530,7 @@ mod test { { let mut txn = env.begin_rw_txn(None).unwrap(); - for &(ref key, ref data) in &items { + for (key, data) in &items { txn.put(db, key, data, WriteFlags::empty()).unwrap(); } txn.commit().unwrap(); @@ -541,7 +541,7 @@ mod test { let cursor = txn.open_ro_cursor(db).unwrap(); assert_eq!( items.clone().into_iter().skip(3).take(3).collect::>(), - cursor.into_iter_dup_of(b"b").into_iter().collect::>>().unwrap() + cursor.into_iter_dup_of(b"b").collect::>>().unwrap() ); let cursor = txn.open_ro_cursor(db).unwrap(); @@ -564,7 +564,7 @@ mod test { { let mut txn = env.begin_rw_txn(None).unwrap(); - for &(ref key, ref data) in &items { + for (key, data) in &items { txn.put(db, key, data, WriteFlags::empty()).unwrap(); } txn.commit().unwrap(); diff --git a/src/environment.rs b/src/environment.rs index 589b5ca..22842c2 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -131,7 +131,7 @@ impl Environment { } /// Create a read-only transaction for use with the environment. - pub fn begin_ro_txn(&self) -> Result { + pub fn begin_ro_txn(&self) -> Result> { RoTransaction::new(self) } @@ -159,18 +159,50 @@ impl Environment { /// Create a read-write transaction for use with the environment. This method will block while /// there are any other read-write transactions open on the environment. - pub fn begin_rw_txn(&self, headroom: Option) -> Result { + pub fn begin_rw_txn(&self, headroom: Option) -> Result> { + self.begin_rw_txn_generic(headroom, false, false) + } + + /// Create a read-write transaction for use with the environment. This method will block while + /// there are any other read-write transactions open on the environment. + /// + /// This is a more generic version of `begin_rw_txn`, which allows to optionally pass the MDB_NOSYNC + /// or MDB_NOMETASYNC flags to the underlying call to `mdb_txn_begin`. Passing the flags here has + /// the same effect as passing them when opening the environment, but only this particular transaction + /// will be affected. Same caveats apply: + /// 1) If MDB_NOSYNC is passed, a system crash may undo an already committed tx or corrupt the db, + /// depending on the underlying filesystem (the corruption is possible e.g. on ext4 in "writeback" + /// mode, on NTFS, probably on APFS too). + /// 2) If MDB_NOMETASYNC is passed, a system crash may undo an already committed tx. + /// + /// Note that the flags from the environment are just ORed with those passed to `mdb_txn_begin`, + /// so you can't "undo" a flag from the environment by passing false for one of the parameters here. + pub fn begin_rw_txn_generic( + &self, + headroom: Option, + no_sync: bool, + no_meta_sync: bool, + ) -> Result> { let _lock = self.db_resize_lock.lock().expect("Database resize mutex lock failed"); self.resize_db_if_necessary(headroom)?; - RwTransaction::new(self) + RwTransaction::new(self, no_sync, no_meta_sync) } /// Flush data buffers to disk. /// /// Data is always written to disk when `Transaction::commit` is called, but the operating /// system may keep it buffered. LMDB always flushes the OS buffers upon commit as well, unless - /// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`. - pub fn sync(&mut self, force: bool) -> Result<()> { + /// `MDB_NOSYNC` or `MDB_NOMETASYNC` were passed when opening the environment or creating the + /// transaction. + /// + /// Note: + /// * If the environment was opened with `MDB_NOSYNC`, `sync` will do nothing unless + /// `force` is set to true. + /// * It will effectively "fix" the potential consistency issues introduced by previous + /// `MDB_NOSYNC` commits (by ensuring that all transaction data has been written to disk). + /// * It is independent from the transactions machinery and can be called concurrently + /// with transaction creation or committing or with itself. + pub fn sync(&self, force: bool) -> Result<()> { unsafe { lmdb_result(ffi::mdb_env_sync(self.env(), i32::from(force))) } } @@ -521,7 +553,7 @@ impl Drop for Environment { } /////////////////////////////////////////////////////////////////////////////////////////////////// -//// Environment Builder +////// Environment Builder /////////////////////////////////////////////////////////////////////////////////////////////////// /// Options for opening or creating an environment. @@ -727,11 +759,11 @@ mod test { fn test_sync() { let dir = TempDir::new("test").unwrap(); { - let mut env = Environment::new().open(dir.path()).unwrap(); + let env = Environment::new().open(dir.path()).unwrap(); assert!(env.sync(true).is_ok()); } { - let mut env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap(); + let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap(); assert!(env.sync(true).is_err()); } } diff --git a/src/transaction.rs b/src/transaction.rs index 8a94618..fcda93c 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -98,7 +98,7 @@ pub trait Transaction: Sized + private::TransactionSealedProps { } /// Open a new read-only cursor on the given database. - fn open_ro_cursor(&self, db: Database) -> Result { + fn open_ro_cursor(&self, db: Database) -> Result> { RoCursor::new(self, db) } @@ -270,10 +270,19 @@ impl<'env> Drop for RwTransaction<'env> { impl<'env> RwTransaction<'env> { /// Creates a new read-write transaction in the given environment. Prefer /// using `Environment::begin_ro_txn`. - pub(crate) fn new(env: &'env Environment) -> Result> { + pub(crate) fn new(env: &'env Environment, no_sync: bool, no_meta_sync: bool) -> Result> { let mut txn = ptr::null_mut(); + + let mut flags = EnvironmentFlags::empty(); + if no_sync { + flags |= EnvironmentFlags::NO_SYNC; + } + if no_meta_sync { + flags |= EnvironmentFlags::NO_META_SYNC; + } + unsafe { - lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), EnvironmentFlags::empty().bits(), &mut txn))?; + lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), flags.bits(), &mut txn))?; Ok(RwTransaction { txn, env, @@ -305,7 +314,7 @@ impl<'env> RwTransaction<'env> { } /// Opens a new read-write cursor on the given database and transaction. - pub fn open_rw_cursor(&mut self, db: Database) -> Result { + pub fn open_rw_cursor(&mut self, db: Database) -> Result> { RwCursor::new(self, db) } @@ -414,7 +423,7 @@ impl<'env> RwTransaction<'env> { } /// Begins a new nested transaction inside of this transaction. - pub fn begin_nested_txn(&mut self) -> Result { + pub fn begin_nested_txn(&mut self) -> Result> { let mut nested: *mut ffi::MDB_txn = ptr::null_mut(); unsafe { let env: *mut ffi::MDB_env = ffi::mdb_txn_env(self.txn()); diff --git a/src/transaction_guard.rs b/src/transaction_guard.rs index 0a5e3dd..91fbd17 100644 --- a/src/transaction_guard.rs +++ b/src/transaction_guard.rs @@ -64,7 +64,7 @@ struct SpinLock<'a> { } impl<'a> SpinLock<'a> { - fn new(lock: &'a AtomicBool) -> SpinLock { + fn new(lock: &'a AtomicBool) -> SpinLock<'a> { while lock.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).unwrap_or(true) { std::thread::yield_now(); }