rmux-server 0.10.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use std::sync::{Arc, Mutex};

use rmux_proto::PaneTarget;
use tokio::sync::Notify;

use super::{ActiveAttachIdentity, RequestHandler};

#[derive(Debug, Default)]
pub(in crate::handler) struct ClockModeIdentityPause {
    reached: Notify,
    release: Notify,
}

impl ClockModeIdentityPause {
    pub(in crate::handler) async fn wait_until_reached(&self) {
        self.reached.notified().await;
    }

    pub(in crate::handler) fn release(&self) {
        self.release.notify_one();
    }
}

type LiveExitPauseKey = (usize, ActiveAttachIdentity);
type ExitCommitPauseKey = (usize, PaneTarget);

static LIVE_EXIT_PAUSES: Mutex<Vec<(LiveExitPauseKey, Arc<ClockModeIdentityPause>)>> =
    Mutex::new(Vec::new());
static EXIT_COMMIT_PAUSES: Mutex<Vec<(ExitCommitPauseKey, Arc<ClockModeIdentityPause>)>> =
    Mutex::new(Vec::new());
static RESTORE_COMMIT_PAUSES: Mutex<Vec<(ExitCommitPauseKey, Arc<ClockModeIdentityPause>)>> =
    Mutex::new(Vec::new());

impl RequestHandler {
    pub(in crate::handler) fn install_live_clock_mode_exit_pause_for_test(
        &self,
        identity: ActiveAttachIdentity,
    ) -> Arc<ClockModeIdentityPause> {
        install_pause((self.test_handler_key(), identity), &LIVE_EXIT_PAUSES)
    }

    pub(in crate::handler) async fn pause_before_live_clock_mode_exit_for_test(
        &self,
        identity: ActiveAttachIdentity,
    ) {
        pause_once(&(self.test_handler_key(), identity), &LIVE_EXIT_PAUSES).await;
    }

    pub(in crate::handler) fn install_clock_mode_exit_commit_pause_for_test(
        &self,
        target: PaneTarget,
    ) -> Arc<ClockModeIdentityPause> {
        install_pause((self.test_handler_key(), target), &EXIT_COMMIT_PAUSES)
    }

    pub(in crate::handler) async fn pause_after_clock_mode_exit_commit_for_test(
        &self,
        target: &PaneTarget,
    ) {
        pause_once(
            &(self.test_handler_key(), target.clone()),
            &EXIT_COMMIT_PAUSES,
        )
        .await;
    }

    pub(in crate::handler) fn install_clock_mode_restore_commit_pause_for_test(
        &self,
        target: PaneTarget,
    ) -> Arc<ClockModeIdentityPause> {
        install_pause((self.test_handler_key(), target), &RESTORE_COMMIT_PAUSES)
    }

    pub(in crate::handler) async fn pause_before_clock_mode_restore_commit_for_test(
        &self,
        target: &PaneTarget,
    ) {
        pause_once(
            &(self.test_handler_key(), target.clone()),
            &RESTORE_COMMIT_PAUSES,
        )
        .await;
    }

    fn test_handler_key(&self) -> usize {
        Arc::as_ptr(&self.active_attach) as usize
    }
}

fn install_pause<K: Eq + Clone>(
    key: K,
    registry: &Mutex<Vec<(K, Arc<ClockModeIdentityPause>)>>,
) -> Arc<ClockModeIdentityPause> {
    let pause = Arc::new(ClockModeIdentityPause::default());
    let mut pauses = registry.lock().expect("clock-mode identity pause lock");
    pauses.retain(|(installed, _)| installed != &key);
    pauses.push((key, pause.clone()));
    pause
}

async fn pause_once<K: Eq>(key: &K, registry: &Mutex<Vec<(K, Arc<ClockModeIdentityPause>)>>) {
    let pause = {
        let mut pauses = registry.lock().expect("clock-mode identity pause lock");
        pauses
            .iter()
            .position(|(installed, _)| installed == key)
            .map(|index| pauses.remove(index).1)
    };
    let Some(pause) = pause else {
        return;
    };
    pause.reached.notify_one();
    pause.release.notified().await;
}