Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronizer API changes #5

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fn main() {
let mut synchronizer = Synchronizer::new("/tmp/hello_world");

// Read data from shared memory
let data = unsafe { synchronizer.read::<HelloWorld>() }.expect("failed to read data");
let data = unsafe { synchronizer.read::<HelloWorld>(false) }.expect("failed to read data");

// Access fields of the struct
println!("version: {} | messages: {:?}", data.version, data.messages);
Expand Down
2 changes: 1 addition & 1 deletion src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::synchronizer::SynchronizerError::*;
/// - data size (<549 GB) - 39 bits
/// - data checksum - 24 bits
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct InstanceVersion(u64);
pub struct InstanceVersion(pub(crate) u64);

const DATA_SIZE_BITS: usize = 39;
const DATA_CHECKSUM_BITS: usize = 24;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
//! To get started with `mmap-sync`, please see the [examples](https://github.com/cloudflare/mmap-sync/tree/main/examples) provided.
mod data;
pub mod guard;
mod instance;
pub mod instance;
mod state;
pub mod synchronizer;
43 changes: 39 additions & 4 deletions src/synchronizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ impl Synchronizer {

/// Reads and returns an `entity` struct from mapped memory wrapped in `ReadGuard`.
///
/// # Parameters
/// - `check_bytes`: Whether to check that `entity` bytes can be safely read for type `T`,
/// `false` - bytes check will not be performed (faster, but less safe),
/// `true` - bytes check will be performed (slower, but safer).
///
/// # Safety
///
/// This method is marked as unsafe due to the potential for memory corruption if the returned
Expand All @@ -169,7 +174,7 @@ impl Synchronizer {
/// `rkyv::archived_root` function, which has its own safety considerations. Particularly, it
/// assumes the byte slice provided to it accurately represents an archived object, and that the
/// root of the object is stored at the end of the slice.
pub unsafe fn read<T>(&mut self) -> Result<ReadResult<T>, SynchronizerError>
pub unsafe fn read<T>(&mut self, check_bytes: bool) -> Result<ReadResult<T>, SynchronizerError>
where
T: Archive,
T::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
Expand All @@ -187,14 +192,28 @@ impl Synchronizer {
let (data, switched) = self.data_container.data(version)?;

// fetch entity from data using zero-copy deserialization
let entity = archived_root::<T>(data);
let entity = match check_bytes {
false => archived_root::<T>(data),
true => check_archived_root::<T>(data).map_err(|_| FailedEntityRead)?,
};

Ok(ReadResult::new(guard, entity, switched))
}

/// Returns current `InstanceVersion` stored within the state, useful for detecting
/// whether synchronized `entity` has changed.
pub fn version(&mut self) -> Result<InstanceVersion, SynchronizerError> {
// fetch current state from mapped memory
let state = self.state_container.state(false)?;

// fetch current version
state.version()
}
}

#[cfg(test)]
mod tests {
use crate::instance::InstanceVersion;
use crate::synchronizer::Synchronizer;
use bytecheck::CheckBytes;
use rand::distributions::Uniform;
Expand Down Expand Up @@ -259,7 +278,7 @@ mod tests {
let mut entity_generator = MockEntityGenerator::new(3);

// check that `read` returns error when writer didn't write yet
let res = unsafe { reader.read::<MockEntity>() };
let res = unsafe { reader.read::<MockEntity>(false) };
assert!(res.is_err());
assert_eq!(
res.err().unwrap().to_string(),
Expand All @@ -274,6 +293,10 @@ mod tests {
assert_eq!(reset, false);
assert!(Path::new(&state_path).exists());
assert!(!Path::new(&data_path_1).exists());
assert_eq!(
reader.version().unwrap(),
InstanceVersion(15768700985330904896)
);

// check that first time scoped `read` works correctly and switches the data
fetch_and_assert_entity(&mut reader, &entity, true);
Expand All @@ -289,6 +312,10 @@ mod tests {
assert!(Path::new(&state_path).exists());
assert!(Path::new(&data_path_0).exists());
assert!(Path::new(&data_path_1).exists());
assert_eq!(
reader.version().unwrap(),
InstanceVersion(7331894278219651425)
);

// check that another scoped `read` works correctly and switches the data
fetch_and_assert_entity(&mut reader, &entity, true);
Expand All @@ -298,11 +325,19 @@ mod tests {
let (size, reset) = writer.write(&entity, Duration::from_secs(1)).unwrap();
assert!(size > 0);
assert_eq!(reset, false);
assert_eq!(
reader.version().unwrap(),
InstanceVersion(9949249822303202528)
);

let entity = entity_generator.gen(200);
let (size, reset) = writer.write(&entity, Duration::from_secs(1)).unwrap();
assert!(size > 0);
assert_eq!(reset, false);
assert_eq!(
reader.version().unwrap(),
InstanceVersion(16072265150643592177)
);

fetch_and_assert_entity(&mut reader, &entity, true);
}
Expand All @@ -312,7 +347,7 @@ mod tests {
expected_entity: &MockEntity,
expected_is_switched: bool,
) {
let actual_entity = unsafe { synchronizer.read::<MockEntity>().unwrap() };
let actual_entity = unsafe { synchronizer.read::<MockEntity>(false).unwrap() };
assert_eq!(actual_entity.map, expected_entity.map);
assert_eq!(actual_entity.version, expected_entity.version);
assert_eq!(actual_entity.is_switched(), expected_is_switched);
Expand Down