Skip to content

GeminiLab/soft-cycle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

soft-cycle

Crates.io docs.rs License: MIT CI

A small async Rust crate for coordinating soft restarts and graceful shutdowns around a shared SoftCycleController, designed for async runtimes where many tasks need to observe lifecycle transitions without blocking:

  • try_notify(payload) publishes a lifecycle notification and wakes current listeners, with an optional payload.
  • try_clear() transitions the controller back to the non-notified state.
  • listener() returns a future that resolves when being notified with the observed payload.

Usage

Use [SoftCycleController::new] to create a new controller instance. Call try_notify to publish a notification with a payload and get Ok(sequence_number) on success (or Err(payload) if already notified), where sequence_number is a monotonically increasing number starting from 0. Call try_clear to clear the notified state and get Ok(sequence_number) for the cleared notification (or Err(()) if not currently notified). Call listener to create a [SoftCycleListener] future that resolves with Ok(payload) when a notification is observed.

Guarantees

  • Linearizable order: All try_notify and try_clear operations are linearizable with respect to a single global order.
  • Non-blocking: try_notify and try_clear are synchronous and never block.
  • Listener completion: A listener created after a notification and before the next clearance completes immediately with the current payload. A listener created after a clear and before the next notification completes in a finite number of polls (usually one) after the next try_notify. If multiple notify/clear cycles occur after a listener is created, it returns one of those payloads (no guarantee of returning the earliest or latest); this is a rare scenario and is not likely to happen unless try_notify and try_clear are called multiple times in a very short time frame.

Features

  • global_instance (default): Enables a process-wide default controller and the async free functions [get_lifetime_controller], [try_restart], [try_shutdown], [listener], and [clear] at the crate root. The global controller uses SoftCycleMessage as the payload type, which contains Shutdown and Restart variants.

Example

use soft_cycle::{SoftCycleController, SoftCycleMessage};
use std::sync::Arc;
use tokio::time::{sleep, Duration, timeout};

#[tokio::main]
async fn main() {
    let controller = Arc::new(SoftCycleController::<SoftCycleMessage>::new());

    // Worker that reacts to notifications.
    let worker_controller = controller.clone();
    let worker = tokio::spawn(async move {
        loop {
            let payload = worker_controller.listener().await.unwrap();
            match payload {
                SoftCycleMessage::Shutdown => {
                    println!("worker: shutdown");
                    break;
                }
                SoftCycleMessage::Restart => {
                    println!("worker: restart");
                }
            }
        }
    });

    // Producer notifies restart.
    assert_eq!(controller.try_notify(SoftCycleMessage::Restart), Ok(0));
    // Already notified, returns Err.
    assert_eq!(controller.try_notify(SoftCycleMessage::Restart), Err(SoftCycleMessage::Restart));
    // Clear when restart handling phase is done.
    assert_eq!(controller.try_clear(), Ok(0));
    // Already cleared, returns Err.
    assert_eq!(controller.try_clear(), Err(()));

    sleep(Duration::from_millis(100)).await;

    // Producer notifies restart again.
    assert_eq!(controller.try_notify(SoftCycleMessage::Restart), Ok(1));
    // Clear when restart handling phase is done.
    assert_eq!(controller.try_clear(), Ok(1));

    sleep(Duration::from_millis(100)).await;

    // Producer notifies shutdown.
    assert_eq!(controller.try_notify(SoftCycleMessage::Shutdown), Ok(2));
    // Optional: wait for worker to observe shutdown.
    timeout(Duration::from_secs(2), worker).await.unwrap().unwrap();
}

About

Async controller for coordinating soft restarts and graceful shutdowns with shared listeners

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages