v0.9.8 — Surface Finish #5
jamesgober
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Release Notes for v0.9.8 - Surface Finish
Version 0.9.8 - 2026-05-12
The ergonomic pass. Eight new methods land on the public surface, all additive, all carrying their full safety contract and a working code example.
open_or_createanswers the most common question every user asks first ("how do I open if it's there and create if it's not?") with one call.from_fileis the escape hatch for callers who already opened the file with their ownOpenOptions(O_DIRECT,O_NOATIME, an inherited fd from a parent process) and want to mmap it without re-opening.unmapis the inverse: consume the mapping, drop the underlying memory in safe order, hand back the bareFileso the caller can keep using it.flush_policyandpending_bytesexpose the durability accumulator for diagnostics and dashboards.as_ptr/as_mut_ptrare the FFI escape hatches for handing the mapping to a C library by base pointer + length.prefetch_rangeissuesposix_fadvise(POSIX_FADV_WILLNEED)on Linux to warm the page cache from the file side, complementary to the existingadvise(WillNeed)that warms it from the VM side.Under the surface, a real bug closed in the time-based flusher: a
Durationsubtraction that could underflow and panic ifthread::sleepovershot under heavy scheduler contention. Fixed withsaturating_sub. Bounds-check helpers (ensure_in_bounds,slice_range) and the small-and-hot accessors (len,is_empty,mode,flush_policy,pending_bytes) gained#[inline]so the optimiser can fold them into the call site every time. The two-branch bounds check merged into a singlesaturating_addcomparison. Audit items E1, E2, E6, E7, F2, F5, and F9 all close in this release.Highlights
open_or_create(path, default_size)opens the file if it exists, creates it atdefault_sizebytes if it does not. The existing-file path ignoresdefault_sizeand keeps the file's current length. The builder gets a matching terminal method:MemoryMappedFile::builder(path).mode(...).size(...).flush_policy(...).open_or_create().from_file(file, mode, path)wraps a pre-openedstd::fs::Filein aMemoryMappedFile. Callers needing customOpenOptions(Direct I/O, no-atime, security descriptors, inherited file descriptors) construct theFilethemselves and hand it off. Thepathargument is informational forpath()/ error messages; no syscall happens on it.unmap(self) -> Result<File, Self>consumes the mapping and returns the underlyingFile. Drop order is enforced: the background flusher stops first, then the mapping releases its virtual address space, then the file handle is yielded to the caller. ReturnsErr(self)unchanged if otherMemoryMappedFileclones are alive (theFileis shared viaArc<Inner>and cannot be extracted while other handles hold references).flush_policy()/pending_bytes()are#[inline]O(1)accessors over the durability accumulator. Useful for observability dashboards on long-running writers: pollpending_bytes()to see how close you are to the next auto-flush underEveryBytes/EveryWrites.unsafe fn as_ptr(&self) -> *const u8andunsafe fn as_mut_ptr(&self) -> Result<*mut u8>expose raw base pointers for FFI use cases that need aconst void */void *plus length. The rustdoc spells out the safety contract: do not dereference pastlen(), do not hold the pointer acrossresize(), do not alias the mutable pointer with any live Rust&reference to the same bytes.prefetch_range(offset, len)issuesposix_fadvise(POSIX_FADV_WILLNEED)against the file descriptor on Linux (and Android), kicking off kernel-side readahead. Documented as a no-op on other platforms. Bounds-checked. Complementary toadvise(MmapAdvice::WillNeed): file-side readahead viaposix_fadviseversus VM-side viamadvise. Issuing both helps cold reads of huge files.tests/ergonomic_api.rscover every new method, both happy and error paths: open_or_create on both create / open paths, builder open_or_create on both, from_file across RO / RW / zero-length, unmap unique vs shared, flush_policy default vs explicit, pending_bytes through a threshold-crossing write, as_ptr / as_mut_ptr roundtrips againstread_into, and prefetch_range in-bounds / OOB / zero-length.Performance
#[inline]-ed.ensure_in_boundsandslice_rangeare called from every bounds-checked public method (as_slice,as_slice_mut,read_into,update_region,flush_range,touch_pages_range,prefetch_range, advise, lock, segment access). Inlining removes the function-call boundary on every read/write. The two-branch bounds check also collapsed into a singlesaturating_addcomparison: the previous form didif offset > totalthenif offset + len > total; the new form checksoffset + len > total || offset > totalin one expression (saturating-add catches the overflow case the first branch was redundantly guarding against).#[inline]:len(),is_empty(),mode(),flush_policy(),pending_bytes(). All trivial (one field read or one lock read of au64), so inlining is a clear win.align_upmarked#[inline]: called fromflush_range(microflush page alignment),touch_pages_range, and prefetch alignment paths. Trivial bit math.Bug fix
flush::TimeBasedFlusherDuration underflow. The thread loop computed the next sleep slice asshutdown_poll.min(interval - elapsed). Ifthread::sleepovershot (heavy scheduler contention, system suspend/resume, oversubscribed runtime)elapsedcould exceedintervaland the subtraction would panic on Duration underflow, killing the flusher thread. Fixed by usinginterval.saturating_sub(elapsed): when overshoot occurs the remaining slice clamps to zero and the loop yields immediately, re-checks the shutdown flag, and either fires the callback or exits. No behavioural change in the common case.Tests
--all-features(up from 101 in 0.9.7), 4 ignored (3 polling-watch tests gated on Windows mtime granularity, 1 hugepages fallback), 0 failed.--no-default-featuresand--no-default-features --features "cow locking advise"both clean locally; doctest counts grew from 13 to 16 with the new method examples.cargo fmt --checkclean.cargo clippy --all-targets --all-features -D warningsclean on default lints.cargo +1.75 build --all-featuresclean.Documentation
Cargo.tomlSEO sweep. Description leads with the unique selling point ("Zero-copy memory-mapped file I/O for Rust"), names the supported platforms, and lists the concrete use cases (databases, log structures, caches, game runtimes, IPC). Keywords tightened to the five highest-volume search terms:mmap,memory-mapped,zero-copy,filesystem,io. Categories:filesystem,data-structures,concurrency,database-implementations.open_or_createas the everyday pattern alongsideopen_ro.docs/API.mdhas full sections for all eight new methods, TOC updated, install snippets bumped to 0.9.8, Version History entry added.REPS.mdsection 4 now lists every public method, including the new ones with// Since 0.9.8markers and the builder addition.Notes
posix_fadviseuses the already-requiredlibccrate.MappedSliceandMappedSliceMutare re-exported from the crate root since 0.9.7; that has not changed.unsafeexposure of raw base pointers viaas_ptr/as_mut_ptris a deliberate FFI escape hatch. The Rust API surface remains safe; the unsafe marker on those two methods forces callers to acknowledge the documented contract.Deferred (with documented reason)
new_anonymous(size)is the one item from the audit's ergonomic / functionality cluster that did not land in this release. The reason is structural: anonymous mappings have no backingFileand no meaningfulPath, so adopting them requires changingInner.file: FiletoOption<File>and threading sentinel-path handling through the resize / prefetch / async-flush paths. That refactor is sized for its own focused milestone rather than rolled into the ergonomic pass.UnsafeCelldesign). The currentRwLock<MmapMut>design is sound and bounded; reads on RW mappings are concurrent across readers thanks to parking_lot's RwLock. Replacing the lock entirely is a memory-model question (do we accept torn reads from concurrent intra-process writers?), not a tuning question, and is rescoped to a 1.0 design conversation.ReadDirectoryChangesW) and fuzz / MIRI runs remain on the roadmap for 0.9.9 and 0.9.10 respectively.Full Changelog: v0.9.7...v0.9.8
This discussion was created from the release v0.9.8 — Surface Finish.
Beta Was this translation helpful? Give feedback.
All reactions