hdf5-pure 0.21.1

Pure-Rust HDF5 library: read, write, and edit files in place (WASM-compatible, no C dependencies)
Documentation
//! OS advisory file-locking behavior for the in-place editor (issue #73). Uses
//! only hdf5-pure (no reference C library), so it runs on every target.
//!
//! Only `EditSession` takes a lock (exclusive); `SwmrWriter` and the readers take
//! none. Assertions here are written to hold on both Unix (advisory `flock`) and
//! Windows (mandatory `LockFileEx`): in particular we never read a file while an
//! editor still holds its lock, because that read is permitted on Unix but
//! blocked by the OS on Windows.

use hdf5_pure::{EditSession, Error, File, FileBuilder, FileLocking, SwmrWriter};
use tempfile::tempdir;

/// A plain, in-place-editable starter file.
fn write_starter(path: &std::path::Path) {
    let mut b = FileBuilder::new();
    b.create_dataset("d").with_i32_data(&[1, 2, 3]);
    b.write(path).unwrap();
}

/// An unlimited Extensible-Array dataset the SWMR append writer can open.
fn write_appendable(path: &std::path::Path) {
    let mut b = FileBuilder::new();
    b.create_dataset("d")
        .with_i32_data(&[0, 1, 2, 3])
        .with_shape(&[4])
        .with_maxshape(&[u64::MAX])
        .with_chunks(&[1]);
    b.write(path).unwrap();
}

#[test]
fn editor_lock_blocks_a_second_editor() {
    let dir = tempdir().unwrap();
    let path = dir.path().join("edit.h5");
    write_starter(&path);

    let session = EditSession::open(&path).unwrap(); // holds the exclusive lock

    // A second editor cannot open the file while the first is alive.
    assert!(
        matches!(EditSession::open(&path), Err(Error::FileLocked(_))),
        "second EditSession::open should be FileLocked"
    );

    drop(session); // releases the lock

    // Once the editor is gone, opening (for edit or read) succeeds again.
    EditSession::open(&path).expect("editor should open after the first is dropped");
    File::open(&path).expect("read should succeed after the editor is dropped");
}

#[test]
fn disabled_locking_takes_no_lock() {
    let dir = tempdir().unwrap();
    let path = dir.path().join("edit.h5");
    write_starter(&path);

    // With locking disabled the editor takes no lock at all, so others can still
    // open the file (true on every platform, since nothing is locked).
    let _editor = EditSession::open_with_locking(&path, FileLocking::Disabled).unwrap();
    File::open(&path).expect("read should succeed: the Disabled editor took no lock");
    EditSession::open_with_locking(&path, FileLocking::Disabled)
        .expect("a second Disabled editor should open: neither took a lock");
}

#[test]
fn swmr_writer_does_not_lock_so_a_reader_can_follow_it() {
    let dir = tempdir().unwrap();
    let path = dir.path().join("swmr.h5");
    write_appendable(&path);

    let mut writer = SwmrWriter::open(&path).unwrap(); // takes no lock
    writer
        .append_i32("d", &(4..20).collect::<Vec<_>>())
        .unwrap();

    // The writer is still alive: a SWMR reader opens and reads concurrently,
    // which only works because neither side holds an OS lock (on Windows a lock
    // would block the read outright).
    let reader = File::open_swmr(&path).expect("open_swmr should coexist with a live SWMR writer");
    assert_eq!(
        reader.dataset("d").unwrap().read_i32().unwrap(),
        (0..20).collect::<Vec<_>>()
    );

    drop(writer);
}