diff --git a/CHANGELOG.md b/CHANGELOG.md index 117b034..91928dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changes + +* Added `Sync` and `Send` implementations to `Snapshot` (pavel-mukhanov) + ## 0.12.2 (2019-05-03) ### Changes diff --git a/src/db.rs b/src/db.rs index ca8430c..c59a871 100644 --- a/src/db.rs +++ b/src/db.rs @@ -104,6 +104,11 @@ pub struct Snapshot<'a> { inner: *const ffi::rocksdb_snapshot_t, } +/// `Send` and `Sync` implementations for `Snapshot` are safe, because `Snapshot` is +/// immutable and can be safely shared between threads. +unsafe impl<'a> Send for Snapshot<'a> {} +unsafe impl<'a> Sync for Snapshot<'a> {} + /// An iterator over a database or column family, with specifiable /// ranges and direction. /// diff --git a/tests/test_db.rs b/tests/test_db.rs index 47dac85..4240d96 100644 --- a/tests/test_db.rs +++ b/tests/test_db.rs @@ -19,7 +19,9 @@ mod util; use libc::size_t; -use rocksdb::{DBVector, Error, IteratorMode, Options, WriteBatch, DB}; +use rocksdb::{DBVector, Error, IteratorMode, Options, Snapshot, WriteBatch, DB}; +use std::sync::Arc; +use std::{mem, thread}; use util::DBPath; #[test] @@ -163,6 +165,45 @@ fn snapshot_test() { } } +#[derive(Clone)] +struct SnapshotWrapper { + snapshot: Arc>, +} + +impl SnapshotWrapper { + fn new(db: &DB) -> Self { + Self { + snapshot: Arc::new(unsafe { mem::transmute(db.snapshot()) }), + } + } + + fn check(&self, key: K, value: &str) -> bool + where + K: AsRef<[u8]>, + { + self.snapshot.get(key).unwrap().unwrap().to_utf8().unwrap() == value + } +} + +#[test] +fn sync_snapshot_test() { + let path = DBPath::new("_rust_rocksdb_sync_snapshottest"); + let db = DB::open_default(&path).unwrap(); + + assert!(db.put(b"k1", b"v1").is_ok()); + assert!(db.put(b"k2", b"v2").is_ok()); + + let wrapper = SnapshotWrapper::new(&db); + let wrapper_1 = wrapper.clone(); + let handler_1 = thread::spawn(move || wrapper_1.check("k1", "v1")); + + let wrapper_2 = wrapper.clone(); + let handler_2 = thread::spawn(move || wrapper_2.check("k2", "v2")); + + assert!(handler_1.join().unwrap()); + assert!(handler_2.join().unwrap()); +} + #[test] fn set_option_test() { let path = DBPath::new("_rust_rocksdb_set_optionstest");