async-listen 0.2.1

Various helpers for writing production-ready servers in rust using async-std.
Documentation
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;

use rand::{Rng, thread_rng};

use async_std::stream::{from_iter, Stream, StreamExt};
use async_std::task;

use async_listen::{ListenExt, backpressure};

fn collect<S: Stream + Unpin>(mut stream: S) -> Vec<S::Item> {
    task::block_on(async {
        let mut result = Vec::new();
        while let Some(item) = stream.next().await {
            result.push(item);
        }
        result
    })
}

fn random_delay() -> Duration {
    Duration::from_millis(thread_rng().gen_range(1, 200))
}

#[test]
fn test_backpressure() {
    let current = Arc::new(AtomicUsize::new(0));
    let top = Arc::new(AtomicUsize::new(0));
    let tasks = collect(
        from_iter(0..100)
        .backpressure(10)
        .map(|(token, _index)| {
            let current = current.clone();
            let top = top.clone();
            task::spawn(async move {
                let size = current.fetch_add(1, Ordering::Acquire) + 1;
                assert!(size <= 10);
                if size == 10 {
                    top.fetch_add(1, Ordering::Relaxed);
                }
                task::sleep(random_delay()).await;
                current.fetch_sub(1, Ordering::Acquire);
                drop(token);
            })
        }));
    for item in tasks {
        task::block_on(item);
    };
    let top = top.load(Ordering::SeqCst);
    println!("Top capacity {}", top);
    assert!(30 < top && top <= 91);
}

#[test]
fn test_apply_backpressure() {
    let current = Arc::new(AtomicUsize::new(0));
    let top = Arc::new(AtomicUsize::new(0));
    let (tx, rx) = backpressure::new(10);
    let tasks = collect(
        from_iter(0..100)
        .apply_backpressure(rx)
        .map(|_index| {
            let token = tx.token();
            let current = current.clone();
            let top = top.clone();
            task::spawn(async move {
                let size = current.fetch_add(1, Ordering::Acquire) + 1;
                assert!(size <= 10);
                if size == 10 {
                    top.fetch_add(1, Ordering::Relaxed);
                }
                task::sleep(random_delay()).await;
                current.fetch_sub(1, Ordering::Acquire);
                drop(token);
            })
        }));
    for item in tasks {
        task::block_on(item);
    };
    let top = top.load(Ordering::SeqCst);
    println!("Top capacity {}", top);
    assert!(30 < top && top <= 91);
}

#[test]
fn test_change_limit() {
    let current = Arc::new(AtomicUsize::new(0));
    let top = Arc::new(AtomicUsize::new(0));
    let (tx, rx) = backpressure::new(10);
    let tasks = collect(
        from_iter(0..100)
        .apply_backpressure(rx)
        .map(|index| {
            let tx = tx.clone();
            let token = tx.token();
            let current = current.clone();
            let top = top.clone();
            task::spawn(async move {
                let size = current.fetch_add(1, Ordering::Acquire) + 1;
                assert!(size <= 20);
                if size == 20 {
                    top.fetch_add(1, Ordering::Relaxed);
                }
                task::sleep(random_delay()).await;
                if index == 20 {
                    tx.set_limit(20);
                } else if index == 60 {
                    tx.set_limit(5);
                }
                current.fetch_sub(1, Ordering::Acquire);
                drop(token);
            })
        }));
    for item in tasks {
        task::block_on(item);
    };
    let top = top.load(Ordering::SeqCst);
    println!("Top capacity {}", top);
    assert!(5 < top && top <= 81);
}