Skip to content

Implement ReadyDoneAware Trait for Lifecycle State Management #53

@thep2p

Description

@thep2p

Overview

The Go implementation has a ReadyDoneAware interface that provides a standard way for components to signal their ready and done states. This is essential for coordinating component lifecycle in complex systems.

Background

Reference implementation: skipgraph-go/modules/component.go

Components need to signal:

  • Ready: Initialization complete, ready to process requests
  • Done: Shutdown complete, no longer processing

Requirements

1. Define ReadyDoneAware Trait

use tokio::sync::watch;

/// Trait for components that have ready and done lifecycle states
pub trait ReadyDoneAware: Send + Sync {
    /// Returns a receiver that will be notified when component is ready.
    /// The channel should send true when ready.
    /// Must be callable multiple times, returning clones of the same receiver.
    fn ready(&self) -> watch::Receiver<bool>;
    
    /// Returns a receiver that will be notified when component is done.
    /// The channel should send true when done.
    /// Must be callable multiple times, returning clones of the same receiver.
    fn done(&self) -> watch::Receiver<bool>;
}

2. Provide Default Implementation Helper

/// Helper struct to manage ready/done state
#[derive(Clone)]
pub struct LifecycleState {
    ready_tx: watch::Sender<bool>,
    ready_rx: watch::Receiver<bool>,
    done_tx: watch::Sender<bool>,
    done_rx: watch::Receiver<bool>,
}

impl LifecycleState {
    pub fn new() -> Self {
        let (ready_tx, ready_rx) = watch::channel(false);
        let (done_tx, done_rx) = watch::channel(false);
        Self { ready_tx, ready_rx, done_tx, done_rx }
    }
    
    /// Signal that the component is ready
    pub fn signal_ready(&self) {
        let _ = self.ready_tx.send(true);
    }
    
    /// Signal that the component is done
    pub fn signal_done(&self) {
        let _ = self.done_tx.send(true);
    }
}

impl ReadyDoneAware for LifecycleState {
    fn ready(&self) -> watch::Receiver<bool> {
        self.ready_rx.clone()
    }
    
    fn done(&self) -> watch::Receiver<bool> {
        self.done_rx.clone()
    }
}

3. Usage Example

pub struct MyComponent {
    lifecycle: LifecycleState,
    // other fields
}

impl MyComponent {
    pub fn new() -> Self {
        Self {
            lifecycle: LifecycleState::new(),
        }
    }
    
    pub async fn initialize(&self) {
        // Do initialization work
        self.lifecycle.signal_ready();
    }
    
    pub async fn shutdown(&self) {
        // Do cleanup work
        self.lifecycle.signal_done();
    }
}

impl ReadyDoneAware for MyComponent {
    fn ready(&self) -> watch::Receiver<bool> {
        self.lifecycle.ready()
    }
    
    fn done(&self) -> watch::Receiver<bool> {
        self.lifecycle.done()
    }
}

Design Considerations

  • Use tokio::sync::watch for efficient multi-consumer notifications
  • Channels must be clonable for multiple observers
  • State transitions are one-way (not ready→ready, not done→done)
  • Components should handle the case where observers never arrive

Testing

  • Test state transitions
  • Test multiple observers
  • Test concurrent access
  • Test with async components

Dependencies

  • tokio (for watch channels)

Priority

High - This is required before implementing the Component trait

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions