notify: improve documentation

This commit is contained in:
Mathieu Trossevin 2024-01-05 22:28:57 +01:00
parent d60d906483
commit 035f354b04
3 changed files with 83 additions and 19 deletions

View file

@ -145,7 +145,7 @@ impl Display for BarrierError {
"Couldn't create pipe to serve as a notify syncronisation barrier : error {errno}."
),
Self::FailedPolling(errno) => write!(f, "poll failed with error : {errno}."),
Self::Timedout => write!(f, "Notification synchronisation timedout."),
Self::Timedout => write!(f, "Notification synchronisation timed out."),
}
}
}
@ -160,3 +160,18 @@ impl Error for BarrierError {
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PollTimeoutFromIntError {
Negative,
}
impl Display for PollTimeoutFromIntError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Negative => write!(f, "This variant of BarrierTimeout cannot be negative but a negative number was passed.")
}
}
}
impl Error for PollTimeoutFromIntError {}

View file

@ -25,10 +25,7 @@ use rustix::{
pipe::PipeFlags,
};
use self::{
error::{BarrierError, NewNotifierError, NotifyError},
types::PollTimeout,
};
use self::error::{BarrierError, NewNotifierError, NotifyError};
pub mod error;
pub mod types;
@ -161,12 +158,19 @@ impl Notifier {
///
/// # Errors
///
/// This function will error out if the passed [`NotifyState`] do not follow the rules set by systemd or if they couldn't be fully sent.
/// This function will error out if the passed [`NotifyState`] couldn't be fully sent.
pub fn notify(&self, state: &[NotifyState<'_>]) -> Result<(), NotifyError> {
self.notify_with_fds(state, &[])
}
pub fn barrier(&self, timeout: PollTimeout) -> Result<(), BarrierError> {
/// Ensure that all previous notifications have been treated by the service manager.
///
/// **This is a blocking call. If you are using it in an async function you might want to use an equivalent of [`tokio::task::spawn_blocking`](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html).**
///
/// # Errors
///
/// This function will error out if the synchronisation mechanism couldn't be created, the synchronising notification failed or the synchronisation timed out.
pub fn barrier(&self, timeout: types::BarrierTimeout) -> Result<(), BarrierError> {
let (to_poll, sent) = rustix::pipe::pipe_with(PipeFlags::CLOEXEC)
.map_err(BarrierError::FailedPipeCreation)?;
@ -178,7 +182,7 @@ impl Notifier {
core::mem::drop(sent);
let to_poll = rustix::event::PollFd::new(&to_poll, rustix::event::PollFlags::HUP);
rustix::event::poll(&mut [to_poll], timeout.into())
rustix::event::poll(&mut [to_poll], timeout.to_raw())
.map_err(BarrierError::FailedPolling)
.and_then(|events| {
if events == 0_usize {
@ -188,14 +192,20 @@ impl Notifier {
})
}
pub fn guard(&self, timeout: PollTimeout) -> NotifyBarrierGuard<'_> {
/// Create a synchronizing RAII guard.
///
/// This create an RAII guard that automatically call [`barrier()`](Self::barrier) with the provided timeout when dropped.
///
/// Do note that this guard's [`Drop`] implementation will block for the provided timeout and ignore all errors returned by [`barrier()`](Self::barrier).
pub fn guard(&self, timeout: types::BarrierTimeout) -> NotifyBarrierGuard<'_> {
NotifyBarrierGuard {
notifier: self,
timeout,
}
}
pub fn with_guard<F, T>(&self, timeout: PollTimeout, f: F) -> T
/// Create a scope at the end of which all notifications sent inside should have been treated by the service manager.
pub fn with_guard<F, T>(&self, timeout: types::BarrierTimeout, f: F) -> T
where
F: FnOnce(NotifyBarrierGuard) -> T,
{
@ -203,9 +213,14 @@ impl Notifier {
}
}
/// RAII guard automatically synchronizing the notifications with the service manager.
///
/// This is created by [`Notifier::guard`].
///
/// Do note that this guard's [`Drop`] implementation will block for the provided timeout and ignore all errors returned by [`barrier()`](Self::barrier).
pub struct NotifyBarrierGuard<'a> {
notifier: &'a Notifier,
timeout: PollTimeout,
timeout: types::BarrierTimeout,
}
impl<'a> NotifyBarrierGuard<'a> {
@ -216,6 +231,7 @@ impl<'a> NotifyBarrierGuard<'a> {
/// # Errors
///
/// This function will error out if the passed [`NotifyState`] do not follow the rules set by systemd or if they couldn't be fully sent.
#[inline]
pub fn notify(&self, state: &[NotifyState<'_>]) -> Result<(), NotifyError> {
self.notifier.notify(state)
}
@ -227,6 +243,7 @@ impl<'a> NotifyBarrierGuard<'a> {
/// # Errors
///
/// This function will error out if the passed [`NotifyState`] do not follow the rules set by systemd or if they couldn't be fully sent.
#[inline]
pub fn notify_with_fds(
&self,
state: &[NotifyState<'_>],
@ -235,11 +252,21 @@ impl<'a> NotifyBarrierGuard<'a> {
self.notifier.notify_with_fds(state, fds)
}
pub fn barrier(&self, timeout: PollTimeout) -> Result<(), BarrierError> {
/// Ensure that all previous notifications have been treated by the service manager.
///
/// **This is a blocking call. If you are using it in an async function you might want to use an equivalent of [`tokio::task::spawn_blocking`](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html).**
///
/// # Errors
///
/// This function will error out if the synchronisation mechanism couldn't be created, the synchronising notification failed or the synchronisation timed out.
#[inline]
pub fn barrier(&self, timeout: types::BarrierTimeout) -> Result<(), BarrierError> {
self.notifier.barrier(timeout)
}
pub fn with_guard<F, T>(&self, timeout: PollTimeout, f: F) -> T
/// Create a scope at the end of which all notifications sent inside should have been treated by the service manager.
#[inline]
pub fn with_guard<F, T>(&self, timeout: types::BarrierTimeout, f: F) -> T
where
F: FnOnce(Self) -> T,
{

View file

@ -1,6 +1,6 @@
//! Newtypes used by [`NotifyState`](super::NotifyState)
use std::fmt::Display;
use core::fmt::Display;
use super::error;
@ -109,15 +109,37 @@ impl Display for Milliseconds {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PollTimeout(i32);
pub enum BarrierTimeout {
Infinite,
Immediate,
NonZero(PollTimeout),
}
impl From<i32> for PollTimeout {
fn from(value: i32) -> Self {
Self(value)
impl BarrierTimeout {
pub(crate) const fn to_raw(self) -> i32 {
match self {
Self::Immediate => 0_i32,
Self::Infinite => -1_i32,
Self::NonZero(PollTimeout(timeout)) => timeout.get(),
}
}
}
impl From<PollTimeout> for i32 {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PollTimeout(core::num::NonZeroI32);
impl TryFrom<core::num::NonZeroI32> for PollTimeout {
type Error = error::PollTimeoutFromIntError;
fn try_from(value: core::num::NonZeroI32) -> Result<Self, Self::Error> {
if value.is_negative() {
return Err(Self::Error::Negative);
}
Ok(Self(value))
}
}
impl From<PollTimeout> for core::num::NonZeroI32 {
#[inline]
fn from(value: PollTimeout) -> Self {
value.0