1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Contains structs that manage the lifetime and connections of a single
//! plugin.

pub mod process;
pub mod thread;

use crate::{
    common::{
        error::{err, Result},
        log::thread::LogThread,
        protocol::{
            PluginAcceptUpstreamRequest, PluginInitializeRequest, PluginInitializeResponse,
            PluginToSimulator, PluginUserInitializeRequest, SimulatorToPlugin,
        },
        types::{ArbCmd, ArbData, PluginType},
    },
    host::configuration::PluginLogConfiguration,
};
use std::fmt::Debug;

#[macro_export]
macro_rules! checked_rpc {
    ($plugin:expr, $message:expr, expect $t:ident) => {
        match $plugin.rpc($message.into()) {
            Ok(PluginToSimulator::$t(x)) => Ok(x),
            Ok(PluginToSimulator::Failure(e)) => err(e),
            Ok(_) => err("Protocol error: unexpected response from plugin"),
            Err(e) => Err(e),
        }
    };
    ($plugin:expr, $message:expr) => {
        match $plugin.rpc($message.into()) {
            Ok(PluginToSimulator::Success) => Ok(()),
            Ok(PluginToSimulator::Failure(e)) => err(e),
            Ok(_) => err("Protocol error: unexpected response from plugin"),
            Err(e) => Err(e),
        }
    };
}

/// The Plugin trait, implemented by all Plugins used in a Simulation.
pub trait Plugin: Debug {
    /// Spawn the Plugin. The Plugin should spawn the actual plugin code and
    /// prepare the communication channel. After spawning the [`rpc`] method
    /// should be available. The simulator will continue to send initialization
    /// requests via the commmunication channel.
    ///
    /// The logger is provided by the simulator, and plugins can use the
    /// reference to the log thread to make the neccesary copies of log senders
    /// to use with their log proxies.
    fn spawn(&mut self, logger: &LogThread) -> Result<()>;

    /// Returns the PluginType of this plugin.
    fn plugin_type(&self) -> PluginType;

    /// Returns the vector of `ArbCmd`s that are to be passed to the plugin's
    /// `initialize()` callback.
    fn init_cmds(&self) -> Vec<ArbCmd>;

    /// Returns the logging configuration for this plugin.
    fn log_configuration(&self) -> PluginLogConfiguration;

    /// Send the SimulatorToPlugin message to the plugin.
    fn rpc(&mut self, msg: SimulatorToPlugin) -> Result<PluginToSimulator>;
}

impl Plugin {
    /// Returns the name of this plugin from its logging configuration.
    pub fn name(&self) -> String {
        self.log_configuration().name
    }

    /// Sends an `PluginInitializeRequest` to this plugin.
    pub fn initialize(
        &mut self,
        logger: &LogThread,
        downstream: &Option<String>,
        seed: u64,
    ) -> Result<PluginInitializeResponse> {
        checked_rpc!(
            self,
            PluginInitializeRequest {
                downstream: downstream.clone(),
                plugin_type: self.plugin_type(),
                seed,
                log_configuration: self.log_configuration(),
                log_channel: logger.get_ipc_sender(),
            },
            expect Initialized
        )
    }

    /// Requests that the plugin waits for the upstream plugin to connect and
    /// establishes the connection.
    pub fn accept_upstream(&mut self) -> Result<()> {
        checked_rpc!(self, PluginAcceptUpstreamRequest)
    }

    /// Send user initialize request to the plugin. This invokes the initialize
    /// callback with the user initialize commands.
    pub fn user_initialize(&mut self) -> Result<()> {
        checked_rpc!(
            self,
            PluginUserInitializeRequest {
                init_cmds: self.init_cmds()
            }
        )
    }

    /// Sends an `ArbCmd` message to this plugin.
    pub fn arb(&mut self, cmd: impl Into<ArbCmd>) -> Result<ArbData> {
        checked_rpc!(
            self,
            cmd.into(),
            expect ArbResponse
        )
    }
}