Repository: tokio-rs/tokio
File: tokio/src/sync/watch.rs (Rust)

Code before:
    /// Modifies watched value, notifying all receivers.
    ///
    /// This can useful for modifying the watched value, without
    /// having to allocate a new instance. Additionally, this
    /// method permits sending values even when there are no receivers.
    ///
    /// # Examples
    /// ```
    /// use tokio::sync::watch;
    ///
    /// struct State {
    ///     counter: usize,
    /// }
    /// let (state_tx, state_rx) = watch::channel(State { counter: 0 });
    /// state_tx.send_modify(|state| state.counter += 1);
    /// assert_eq!(state_rx.borrow().counter, 1);
    /// ```
    pub fn send_modify<F>(&self, func: F)
    where
        F: FnOnce(&mut T),
    {
        {
            // Acquire the write lock and update the value.
            let mut lock = self.shared.value.write().unwrap();
            // Update the value.
            func(&mut lock);

            self.shared.state.increment_version();

            // Release the write lock.
            //
            // Incrementing the version counter while holding the lock ensures
            // that receivers are able to figure out the version number of the
            // value they are currently looking at.
            drop(lock);
        }

        self.shared.notify_rx.notify_waiters();
    }

    /// Sends a new value via the channel, notifying all receivers and returning
    /// the previous value in the channel.
    ///
    /// This can be useful for reusing the buffers inside a watched value.
    /// Additionally, this method permits sending values even when there are no
    /// receivers.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;

Review comment (suggestion):
One question I have about the current design is that this will poison the lock if a panic happens in `func`. We should decide on what the behavior should be in case of a panic and not poison the lock.

Code after:

    /// Modifies watched value, notifying all receivers.
    ///
    /// This can useful for modifying the watched value, without
    /// having to allocate a new instance. Additionally, this
    /// method permits sending values even when there are no receivers.
    ///
    /// # Panics
    ///
    /// This function panics if calling `func` results in a panic.
    /// No receivers are notified if panic occurred, but if the closure has modified
    /// the value, that change is still visible to future calls to `borrow`.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;
    ///
    /// struct State {
    ///     counter: usize,
    /// }
    /// let (state_tx, state_rx) = watch::channel(State { counter: 0 });
    /// state_tx.send_modify(|state| state.counter += 1);
    /// assert_eq!(state_rx.borrow().counter, 1);
    /// ```
    pub fn send_modify<F>(&self, func: F)
    where
        F: FnOnce(&mut T),
    {
        {
            // Acquire the write lock and update the value.
            let mut lock = self.shared.value.write().unwrap();
            // Update the value and catch possible panic inside func.
            let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
                func(&mut lock);
            }));
            // If the func panicked return the panic to the caller.
            if let Err(error) = result {
                // Drop the lock to avoid poisoning it.
                drop(lock);
                panic::resume_unwind(error);
            }

            self.shared.state.increment_version();

            // Release the write lock.
            //
            // Incrementing the version counter while holding the lock ensures
            // that receivers are able to figure out the version number of the
            // value they are currently looking at.
            drop(lock);