dvcompute 2.0.0

Discrete event simulation library (sequential simulation)
Documentation
// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

#[cfg(any(feature="seq_mode", feature="wasm_mode"))]
use std::cell::UnsafeCell;

#[cfg(any(feature="seq_mode", feature="wasm_mode"))]
use std::ops::Deref;

#[cfg(feature="cons_mode")]
use libc::*;

use crate::simulation::error::*;
use crate::simulation::specs::SpecsRepr;
use crate::simulation::point::Point;
use crate::simulation::event::*;

#[cfg(any(feature="seq_mode", feature="wasm_mode"))]
use crate::simulation;

#[cfg(any(feature="seq_mode", feature="wasm_mode"))]
use crate::simulation::utils::priority_queue::PriorityQueue;

/// The event queue for the optimistic Time Warp method.
#[cfg(any(feature="seq_mode", feature="wasm_mode"))]
pub struct EventQueue {

    /// The current event time.
    time: UnsafeCell<f64>,

    /// The priority queue.
    pq: UnsafeCell<PriorityQueue<(f64, isize), EventRepr>>
}

#[cfg(any(feature="seq_mode", feature="wasm_mode"))]
impl EventQueue {

    /// Create a new event queue for the sequential simulation.
    pub fn new(specs: &SpecsRepr) -> EventQueue {
        EventQueue {
            time: UnsafeCell::new(specs.start_time),
            pq: UnsafeCell::new(PriorityQueue::new())
        }
    }

    /// Enqueue the event by the specified activation time and handler generator.
    #[inline]
    pub fn enqueue_event(&self, event_time: f64, priority: isize, comp: EventRepr, p: &Point) {
        assert!(event_time >= p.time, "The event time cannot be less than the current modeling time");
        let pq = self.pq.get();
        unsafe {
            (*pq).enqueue((event_time, - priority), comp);
        }
    }

    /// Enqueue the IO-based event by the specified activation time and handler generator.
    #[inline]
    pub fn enqueue_io_event(&self, event_time: f64, priority: isize, comp: EventRepr, p: &Point) {
        self.enqueue_event(event_time, priority, comp, p)
    }

    /// Run the pending events.
    pub fn run_events(&self, including_current: bool, p: &Point) -> simulation::Result<()> {
        loop {
            let ((t2, pri2), c2) = unsafe {
                let pq = self.pq.get();
                let t0 = self.time.get();
                match (*pq).minimal_key() {
                    Some((t2, _)) if *t2 < *t0 => {
                        panic!("The time value is too small. The event queue is desynchronized");
                    }
                    Some((t2, _)) if *t2 < p.time || (including_current && *t2 == p.time) => {
                        *t0 = *t2;
                    }
                    _ => {
                        break;
                    }
                }
                (*pq).dequeue()
            };
            let run = p.run;
            let specs = &run.specs;
            let t0 = specs.start_time;
            let dt = specs.dt;
            let n2 = ((t2 - t0) / dt).floor() as usize;
            let p2 = Point {
                run: p.run,
                time: t2,
                priority: -pri2,
                minimal_priority: p.minimal_priority,
                iteration: n2,
                phase: -1
            };
            match c2.call_event(&p2) {
                Result::Ok(()) => (),
                Result::Err(Error::Cancel) => (),
                Result::Err(Error::Other(x)) => {
                    match x.deref() {
                        &OtherError::Retry(_) => {
                            return Result::Err(Error::Other(x.clone()))
                        },
                        &OtherError::Panic(_) => {
                            return Result::Err(Error::Other(x.clone()))
                        },
                        &OtherError::IO(_) => {
                            return Result::Err(Error::Other(x.clone()))
                        }
                    }
                }
            }
        }
        Result::Ok(())
    }
}

/// Represents the event queue.
#[cfg(feature="cons_mode")]
pub type EventQueue = c_void;

#[cfg(all(feature="cons_mode", not(feature="cons_core_mode")))]
#[cfg_attr(windows, link(name = "dvcompute_core_cons.dll"))]
#[cfg_attr(not(windows), link(name = "dvcompute_core_cons"))]
extern {

    /// Create a new event queue.
    #[doc(hidden)]
    pub fn create_extern_event_queue(specs: *const SpecsRepr) -> *mut EventQueue;

    /// Delete the event queue.
    #[doc(hidden)]
    pub fn delete_extern_event_queue(queue: *mut EventQueue);

    /// Enqueue a new event.
    #[doc(hidden)]
    pub fn enqueue_extern_event(queue: *mut EventQueue, event_time: f64, priority: isize, comp: EventRepr, p: *const Point);

    /// Enqueue a new IO event.
    #[doc(hidden)]
    pub fn enqueue_extern_io_event(queue: *mut EventQueue, event_time: f64, priority: isize, comp: EventRepr, p: *const Point);

    /// Run the events.
    #[doc(hidden)]
    pub fn run_extern_events(including_current: isize, p: *const Point) -> *mut ErrorRepr;
}

#[cfg(all(feature="cons_mode", feature="cons_core_mode"))]
extern {

    /// Create a new event queue.
    #[doc(hidden)]
    pub fn create_extern_event_queue(specs: *const SpecsRepr) -> *mut EventQueue;

    /// Delete the event queue.
    #[doc(hidden)]
    pub fn delete_extern_event_queue(queue: *mut EventQueue);

    /// Enqueue a new event.
    #[doc(hidden)]
    pub fn enqueue_extern_event(queue: *mut EventQueue, event_time: f64, priority: isize, comp: EventRepr, p: *const Point);

    /// Enqueue a new IO event.
    #[doc(hidden)]
    pub fn enqueue_extern_io_event(queue: *mut EventQueue, event_time: f64, priority: isize, comp: EventRepr, p: *const Point);

    /// Run the events.
    #[doc(hidden)]
    pub fn run_extern_events(including_current: isize, p: *const Point) -> *mut ErrorRepr;
}