nursery 0.0.1

An implemenation of Nathaniel J. Smiths Concurrency primitive for Rust.
Documentation
// Copyright 2019 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! An implementation of Nathaniel J. Smiths Structured Concurrency model:
//! [Notes on Structured Concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/#nurseries-a-structured-replacement-for-go-statements).
//! # Description
//! 
//! The building block of concurrency is called a `Nursery`. They can adopt or schedule
//! concurrent operations. Before a `Nursery` is dropped it will block on all of it's
//! pending concurrent operations. A `Nursery` is itself a concurrent operation so
//! it can be adopted by another `Nursery` to form a hierarchy.
//! 
//! # Example
//!
//! ```rust
//! extern crate nursery;
//! use nursery::thread::{Handle, Pending};
//! use nursery::{Nursery, Waitable};
//! use std::sync::Arc;
//! use std::sync::Mutex;
//! 
//! pub struct Counter {
//!     count: i32,
//! }
//! 
//! impl Counter {
//!     pub fn incr(&mut self) {
//!         self.count += 1;
//!     }
//! }
//! 
//! let counter = Arc::new(Mutex::new(Counter { count: 0 }));
//! {
//!     let h_counter = counter.clone();
//!     let h1 = Pending::new(move || {
//!         let mut c = h_counter.lock().unwrap();
//!         c.incr();
//!     });
//!     let h_counter = counter.clone();
//!     let h2 = Pending::new(move || {
//!         let mut c = h_counter.lock().unwrap();
//!         c.incr();
//!     });
//!     let mut child = Nursery::new();
//!     child.schedule(Box::new(h1));
//!     child.schedule(Box::new(h2));
//!     let mut parent = Nursery::new();
//!     parent.adopt(child.into());
//!     // Before parent is dropped all of the above concurrent operations
//!     // will complete.
//! }
//! assert_eq!(counter.lock().unwrap().count, 2);
//! ```
pub mod thread;

pub use crate::thread::Handle as ThreadHandle;

/// The trait that handles to a concurrent operation must implement.
pub trait Waitable {
    /// Waits for the Waitable operation to finish.
    /// It is expected that implementors will make this an idempotent
    /// operation.
    fn wait(&mut self);
}

/// The trait that as of yet not started concurrent operations must implement.
pub trait Schedulable {
    /// Start should panic if called twice.
    fn start(&mut self) -> Box<dyn Waitable>;
}

/// An RAII aware concurrency primitive.
///
/// Nurseries can adopt or schdule concurrent operations. When a nursery
/// leaves scope it will block on the completion of all of it's child
/// operations.
///
/// A `Nursery` is iteself a concurrent operation and can be adopted
/// by another `Nursery` to form a hierarchy. The adoptive `Nursery` will
/// wait on the adopted `Nursery` before leaving scope.
pub struct Nursery {
    handles: Vec<Box<dyn Waitable>>,
}

impl Nursery {
    /// Constructs a new empty` Nursery`.
    pub fn new() -> Self {
        Self {
            handles: Vec::new(),
        }
    }

    /// Adopt a running `Waitable` into the `Nursery`.
    pub fn adopt(&mut self, h: Box<dyn Waitable>) {
        self.handles.push(h);
    }

    // Schedule an unstarted `Schedulable` concurrent operation.
    pub fn schedule(&mut self, mut h: Box<Schedulable>) {
        let h = h.start();
        self.handles.push(h)
    }
}

impl From<Nursery> for Box<dyn Waitable> {
    fn from(n: Nursery) -> Self {
        Box::new(n)
    }
}

impl Waitable for Nursery {
    // Wait on a Nursery is safe to call more than once.
    fn wait(&mut self) {
        for mut h in self.handles.drain(0..) {
            h.wait();
        }
    }
}

impl Drop for Nursery {
    fn drop(&mut self) {
        self.wait();
    }
}

#[cfg(test)]
mod test;