desim 0.4.0

A discrete-time events simulation framework inspired by Simpy
Documentation
/* Copyright © 2018 Gianmarco Garrisi

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>. */
//! `Resource` trait and some implementations.
//!
//! In many simulated systems, finite resources have to be taken into account.
//! The `Resource` trait can be used to model such entities.
//!
//! When a resource is added to a simulation with the `create_resource` method an ID is returned.
//! The simulation process can request the resource yielding the `Request` effect with the ID.
//!
//! The `Resource` trait allow the implementation of custom resource types.
//! A `SimpleResource` struct provides a basic but useful implementation of the `Resource` trait.
use crate::{Event, SimState};
use std::collections::VecDeque;

/// A simple resource that is allocated based on a first come first served policy.
///
/// When there are no more instances of the resource available, the processes are enqueued in a
/// FIFO and triggered as soon as an instance is released.
///
/// If a resource gets more release then requests, it will panic.
#[derive(Debug)]
pub struct SimpleResource<T> {
    quantity: usize,
    available: usize,
    queue: VecDeque<Event<T>>,
}

/// The resource trait implemented by every Resource of the simulation
pub trait Resource<T> {
    /// This method is called whenever a resource is requested by a process in the simulation.
    ///
    /// It receives an event with current time, the `ProcessId` of the process requesting the
    /// resource and the state generated by the process (with the request effect).
    ///
    /// It returns an optional `Event` that is added to the simulation.
    fn allocate_or_enqueue(&mut self, event: Event<T>) -> Option<Event<T>>;

    /// This method is called by the simulator when the resource is released.
    ///
    /// It will receive the release event with current simulation time, the id of the process
    /// that released the request and the state generated by the process (with the release effect)
    ///
    /// If an optional `Event` is returned, it is scheduled to be simulated.
    fn release_and_schedule_next(&mut self, event: Event<T>) -> Option<Event<T>>;
}

/// A type of resource where processes can push into or pull from
pub trait Store<T> {
    /// This method is called whenever a process of the simulation tries to push something into the store
    ///
    /// It receives an event with current time, the `ProcessId` of the process requesting the
    /// resource and the state generated by the process (with the request effect).
    ///
    /// It receives a mutable reference to a vector of `Event`s to push the events that will
    /// be added to the simulation.
    fn push_or_enqueue_and_schedule_next(
        &mut self,
        event: Event<T>,
        next_events: &mut Vec<Event<T>>,
    );

    /// This method is called whenever a process of the simulation tries to pull something out of the store
    ///
    /// It receives an event with current time, the `ProcessId` of the process requesting the
    /// resource and the state generated by the process (with the request effect).
    ///
    /// It receives a mutable reference to a vector of `Event`s to push the events that will
    /// be added to the simulation.
    fn pull_or_enqueue_and_schedule_next(
        &mut self,
        event: Event<T>,
        next_events: &mut Vec<Event<T>>,
    );
}

impl<T> Resource<T> for SimpleResource<T> {
    fn allocate_or_enqueue(&mut self, event: Event<T>) -> Option<Event<T>> {
        if self.available > 0 {
            self.available -= 1;
            Some(event)
        } else {
            self.queue.push_back(event);
            None
        }
    }
    fn release_and_schedule_next(&mut self, event: Event<T>) -> Option<Event<T>> {
        match self.queue.pop_front() {
            Some(mut request_event) => {
                // some is waiting for the request, schedule it! and schedule the self
                request_event.set_time(event.time());
                Some(request_event)
            }
            None => {
                // no one is waiting for the resorce, restore the availiable and return self
                assert!(self.available < self.quantity);
                self.available += 1;
                None
            }
        }
    }
}

impl<T> SimpleResource<T> {
    /// Create a simple resource of which `quantity` instances are available
    pub fn new(quantity: usize) -> SimpleResource<T> {
        SimpleResource {
            quantity,
            available: quantity,
            queue: VecDeque::new(),
        }
    }
}
/// a class that implement waiting on both request and release
pub struct SimpleStore<T> {
    capacity: usize,
    send_waiting_queue: VecDeque<Event<T>>,
    recv_waiting_queue: VecDeque<Event<T>>,
    value_queue: VecDeque<Event<T>>,
}
impl<T: SimState + Clone> Store<T> for SimpleStore<T> {
    fn push_or_enqueue_and_schedule_next(
        &mut self,
        event: Event<T>,
        next_events: &mut Vec<Event<T>>,
    ) {
        if let Some(recv_waiting) = self.recv_waiting_queue.pop_front() {
            // activate both processes
            // 1. activate the recv_waiting process
            let recv_id = recv_waiting.process();
            let mut recv_event = event.clone();
            recv_event.set_process(recv_id);
            next_events.push(recv_event);

            // 2. activate the send process
            let send_event = event;
            next_events.push(send_event);
        } else {
            // in this case, no recv waiting process
            // if the queue is full, push to waiting queue and return nothing,
            // else, push to the queue and send continue

            if self.value_queue.len() < self.capacity {
                // queue is not full
                self.value_queue.push_back(event.clone());
                next_events.push(event);
            } else {
                // queue is full
                self.send_waiting_queue.push_back(event);
            }
        }
    }

    fn pull_or_enqueue_and_schedule_next(
        &mut self,
        event: Event<T>,
        next_events: &mut Vec<Event<T>>,
    ) {
        let current_time = event.time();
        let current_id = event.process();

        if let Some(mut value) = self.value_queue.pop_front() {
            // At least a value is present, use it to wake up the receiver immediately
            value.set_process(current_id);
            value.set_time(current_time);
            next_events.push(value);

            if let Some(mut waiting) = self.send_waiting_queue.pop_front() {
                // There are waiting senders. Need to do 2 things:
                // 1) move waiting to value queue,
                // 2) activate the waiting process,

                // 1. move waiting to value queue
                self.value_queue.push_back(waiting.clone());
                waiting.set_time(current_time);

                // 2. acitvate the waiting process
                next_events.push(waiting);
            }
        } else {
            // in this case, no value in queue, and no waiting process, put this request to recv_waiting list! and return nothing
            self.recv_waiting_queue.push_back(event);
        }
    }
}
impl<T> SimpleStore<T> {
    pub fn new(capacity: usize) -> Self {
        SimpleStore {
            capacity,
            send_waiting_queue: VecDeque::default(),
            recv_waiting_queue: VecDeque::default(),
            value_queue: VecDeque::default(),
        }
    }
}