Skip to content

Commit 9d839ff

Browse files
committed
Document the atomic_append API / add Safety Section
1 parent 25788c1 commit 9d839ff

1 file changed

Lines changed: 45 additions & 27 deletions

File tree

src/lib.rs

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,52 @@ impl<H, T: Clone> HeaderVec<H, T> {
462462

463463
#[cfg(feature = "atomic_append")]
464464
/// The atomic append API is only enabled when the `atomic_append` feature flag is set (which
465-
/// is the default).
465+
/// is the default). The [`push_atomic()`] or [`extend_from_slice_atomic()`] methods then
466+
/// become available and some internals using atomic operations.
467+
///
468+
/// This API implements interior-mutable appending to a shared `HeaderVec`. To other threads
469+
/// the appended elements are either not seen or all seen at once. Without additional
470+
/// synchronization these appends are racy but memory safe. The intention behind this API is to
471+
/// provide facilities for building other container abstractions the benefit from the shared
472+
/// non blocking nature while being unaffected from the racy semantics or provide synchronization
473+
/// on their own (Eg: reference counted data, interners, streaming parsers, etc). Since the
474+
/// `HeaderVec` is a shared object and we have only a `&self`, it can not be reallocated and moved,
475+
/// therefore appending can only be done within the reserved capacity.
476+
///
477+
/// # Safety
478+
///
479+
/// Only one single thread must try to [`push_atomic()`] or [`extend_from_slice_atomic()`] the
480+
/// `HeaderVec` at at time using the atomic append API's. The actual implementations of this
481+
/// restriction is left to the caller. This can be done by mutexes or guard objects. Or
482+
/// simply by staying single threaded or ensuring somehow else that there is only a single
483+
/// thread using the atomic_appending API.
466484
impl<H, T> HeaderVec<H, T> {
485+
/// Atomically adds an item to the end of the list without reallocation.
486+
///
487+
/// # Errors
488+
///
489+
/// If the vector is full, the item is returned.
490+
///
491+
/// # Safety
492+
///
493+
/// There must be only one thread calling this method at any time. Synchronization has to
494+
/// be provided by the user.
495+
pub unsafe fn push_atomic(&self, item: T) -> Result<(), T> {
496+
// relaxed is good enough here because this should be the only thread calling this method.
497+
let len = self.len_atomic_relaxed();
498+
if len < self.capacity() {
499+
unsafe {
500+
core::ptr::write(self.end_ptr_atomic_mut(), item);
501+
};
502+
let len_again = self.len_atomic_add_release(1);
503+
// in debug builds we check for races, the chance to catch these are still pretty minimal
504+
debug_assert_eq!(len_again, len, "len was updated by another thread");
505+
Ok(())
506+
} else {
507+
Err(item)
508+
}
509+
}
510+
467511
/// Get the length of the vector with `Ordering::Acquire`. This ensures that the length is
468512
/// properly synchronized after it got atomically updated.
469513
#[inline(always)]
@@ -505,32 +549,6 @@ impl<H, T> HeaderVec<H, T> {
505549
fn end_ptr_atomic_mut(&self) -> *mut T {
506550
unsafe { self.start_ptr().add(self.len_atomic_acquire()) as *mut T }
507551
}
508-
509-
/// Atomically adds an item to the end of the list without reallocation.
510-
///
511-
/// # Errors
512-
///
513-
/// If the vector is full, the item is returned.
514-
///
515-
/// # Safety
516-
///
517-
/// There must be only one thread calling this method at any time. Synchronization has to
518-
/// be provided by the user.
519-
pub unsafe fn push_atomic(&self, item: T) -> Result<(), T> {
520-
// relaxed is good enough here because this should be the only thread calling this method.
521-
let len = self.len_atomic_relaxed();
522-
if len < self.capacity() {
523-
unsafe {
524-
core::ptr::write(self.end_ptr_atomic_mut(), item);
525-
};
526-
let len_again = self.len_atomic_add_release(1);
527-
// in debug builds we check for races, the chance to catch these are still pretty minimal
528-
debug_assert_eq!(len_again, len, "len was updated by another thread");
529-
Ok(())
530-
} else {
531-
Err(item)
532-
}
533-
}
534552
}
535553

536554
#[cfg(feature = "atomic_append")]

0 commit comments

Comments
 (0)