169 lines
6.9 KiB
Rust
169 lines
6.9 KiB
Rust
//! ============================================================================
|
|
//! LEARNING GUIDE - PART 1: Basic Usage
|
|
//! ============================================================================
|
|
//!
|
|
//! WHAT YOU'LL LEARN:
|
|
//! - How to create an announcement channel
|
|
//! - How to announce a value
|
|
//! - How to check for values without blocking (try_listen)
|
|
//! - Basic synchronous usage patterns
|
|
//!
|
|
//! PREREQUISITES:
|
|
//! - None - start here!
|
|
//!
|
|
//! KEY CONCEPTS:
|
|
//! - Announcement channel: One sender (Announcer), many receivers (Listeners)
|
|
//! - Oneshot semantics: Can only announce ONCE
|
|
//! - Non-blocking checks: Use try_listen() to check without waiting
|
|
//!
|
|
//! Run with: cargo run --example 01_basic_usage
|
|
|
|
use announcement::Announcement;
|
|
|
|
fn main() {
|
|
println!("=== Basic Announcement Usage ===\n");
|
|
|
|
// STEP 1: Create an announcement channel
|
|
// ========================================
|
|
// The new() method returns TWO things:
|
|
// - announcer: Used ONCE to broadcast a value
|
|
// - announcement: Used to create listeners that receive the value
|
|
//
|
|
// IMPORTANT: The announcer is NOT Clone - you can only use it once!
|
|
// This enforces the "oneshot" semantics at compile time.
|
|
let (announcer, announcement) = Announcement::<i32>::new();
|
|
|
|
println!("Step 1: Created announcement channel");
|
|
println!(" - announcer: Will send the value (used once)");
|
|
println!(" - announcement: Creates listeners (can create many)\n");
|
|
|
|
// STEP 2: Create a listener BEFORE announcing
|
|
// ============================================
|
|
// Listeners can be created at any time - before OR after the value is announced.
|
|
// Each listener is independent and will receive the same value.
|
|
//
|
|
// NOTE: Creating a listener is very cheap (~20ns, just 2 Arc clones).
|
|
let listener1 = announcement.listener();
|
|
|
|
println!("Step 2: Created first listener");
|
|
println!(" - Listener created BEFORE value is announced\n");
|
|
|
|
// STEP 3: Try to listen (non-blocking check)
|
|
// ===========================================
|
|
// try_listen() checks if a value has been announced WITHOUT waiting.
|
|
// Returns:
|
|
// - Some(value) if announced
|
|
// - None if not yet announced
|
|
//
|
|
// IMPORTANT: This is a lock-free operation (~5-10ns), very fast!
|
|
match listener1.try_listen() {
|
|
Some(value) => {
|
|
println!("Step 3: Got value: {}", value);
|
|
}
|
|
None => {
|
|
println!("Step 3: No value yet (as expected)");
|
|
println!(" - try_listen() returns None when nothing announced\n");
|
|
}
|
|
}
|
|
|
|
// STEP 4: Check if announced (alternative pattern)
|
|
// =================================================
|
|
// The announcement itself can check if a value was announced.
|
|
// This is useful in the sending/coordinating code.
|
|
if !announcement.is_announced() {
|
|
println!("Step 4: Confirmed - nothing announced yet\n");
|
|
}
|
|
|
|
// STEP 5: Announce the value
|
|
// ===========================
|
|
// This is the ONESHOT broadcast operation.
|
|
// - Stores the value in shared memory (Arc<OnceLock>)
|
|
// - Wakes up any waiting listeners (we don't have any waiting yet)
|
|
// - Can only be called ONCE (enforced by consuming the announcer)
|
|
//
|
|
// PERFORMANCE: ~10-40ns, completely non-blocking
|
|
println!("Step 5: Announcing value 42...");
|
|
announcer.announce(42).unwrap();
|
|
println!(" - Value 42 broadcasted to all listeners\n");
|
|
|
|
// NOTE: At this point, the announcer has been consumed.
|
|
// We can't announce again - the compiler prevents it!
|
|
// Try uncommenting this line to see the error:
|
|
// announcer.announce(99).unwrap(); // ERROR: value used after move
|
|
|
|
// STEP 6: Verify announcement happened
|
|
// =====================================
|
|
if announcement.is_announced() {
|
|
println!("Step 6: Confirmed - value has been announced\n");
|
|
}
|
|
|
|
// STEP 7: Retrieve the value
|
|
// ===========================
|
|
// Now try_listen() will return Some(value) immediately.
|
|
// The value is cloned from shared storage.
|
|
//
|
|
// IMPORTANT: For expensive-to-clone types, wrap in Arc<T> (see example 04).
|
|
match listener1.try_listen() {
|
|
Some(value) => {
|
|
println!("Step 7: Listener 1 received: {}", value);
|
|
println!(" - try_listen() now returns Some(42)\n");
|
|
}
|
|
None => {
|
|
println!("ERROR: This shouldn't happen!");
|
|
}
|
|
}
|
|
|
|
// STEP 8: Create additional listeners AFTER announcing
|
|
// =====================================================
|
|
// Listeners can be created even after the value is announced.
|
|
// They'll get the value immediately when they check.
|
|
//
|
|
// This is a KEY FEATURE - late joiners still get the value!
|
|
let listener2 = announcement.listener();
|
|
let listener3 = announcement.listener();
|
|
|
|
println!("Step 8: Created TWO more listeners AFTER announcing");
|
|
println!(" - These 'late joiners' still get the value\n");
|
|
|
|
// STEP 9: All listeners receive the same value
|
|
// =============================================
|
|
// Every listener, no matter when created, gets the same value.
|
|
// This is the "broadcast" part of oneshot broadcast.
|
|
println!("Step 9: All listeners can retrieve the value:");
|
|
println!(" - Listener 1: {:?}", listener1.try_listen());
|
|
println!(" - Listener 2: {:?}", listener2.try_listen());
|
|
println!(" - Listener 3: {:?}", listener3.try_listen());
|
|
println!("\n All received Some(42)!\n");
|
|
|
|
// STEP 10: Listeners can be called multiple times
|
|
// ================================================
|
|
// Calling try_listen() multiple times on the same listener
|
|
// returns the value each time (cloning it).
|
|
//
|
|
// TIP: This is safe but clones the value each time.
|
|
// For large types, use Arc<T> to avoid repeated cloning (see example 04).
|
|
println!("Step 10: Calling try_listen() again:");
|
|
println!(" - First call: {:?}", listener1.try_listen());
|
|
println!(" - Second call: {:?}", listener1.try_listen());
|
|
println!(" - Third call: {:?}", listener1.try_listen());
|
|
println!("\n Same value every time!\n");
|
|
|
|
// SUMMARY
|
|
// =======
|
|
println!("=== Summary ===");
|
|
println!("✓ Created announcement channel (announcer + announcement)");
|
|
println!("✓ Created listeners before AND after announcing");
|
|
println!("✓ Used try_listen() to check non-blocking");
|
|
println!("✓ Announced value ONCE (oneshot semantics)");
|
|
println!("✓ All listeners received the same value");
|
|
println!("\nNEXT: See example 02 for async listening with .await");
|
|
|
|
// EXPECTED OUTPUT:
|
|
// ================
|
|
// You should see all steps execute in order, showing:
|
|
// - try_listen() returns None before announcing
|
|
// - try_listen() returns Some(42) after announcing
|
|
// - All listeners get the value, even late joiners
|
|
// - Multiple calls to try_listen() return the same value
|
|
}
|