A flag type that supports waiting asynchronously

How about this one implemented using tokio::sync::watch?

use tokio::sync::watch::{self, Sender};

pub struct Flag {
    sender: Sender<bool>,
}

impl Flag {
    /// Creates a new flag object.
    pub fn new(enabled: bool) -> Self {
        Self {
            sender: watch::channel(enabled).0,
        }
    }

    /// Enables the flag.
    pub fn enable(&self) {
        self.sender.send_if_modified(|value| {
            if *value {
                false
            } else {
                *value = true;

                true
            }
        });
    }

    /// Disables the flag.
    pub fn disable(&self) {
        self.sender.send_if_modified(|value| {
            *value = false;

            false
        });
    }

    /// Waits the flag to become enabled.
    pub async fn wait_enabled(&self) {
        if !*self.sender.borrow() {
            let mut receiver = self.sender.subscribe();

            if !*receiver.borrow() {
                receiver.changed().await.ok();
            }
        }
    }
}

Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ecdac1b2db1edfe25cbed0599198c990.

This one has two disadvantages:

  • The boolean value is protected by a lock, so the performance could be affected.
  • There is no way to name the wait_enabled Future, since the Future returned by receiver.changed() cannot be named.

I still prefer the one with AtomicBool, if its correctness can be justified.

1 Like