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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
use eva_common::prelude::*;
use parking_lot::Mutex;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use uuid::Uuid;
/// Action termination helper for EVA ICS controller services
pub struct Actt {
pending_by_oid: HashMap<OID, Mutex<HashMap<Uuid, PendingAction>>>,
pending_by_uuid: Mutex<HashMap<Uuid, PendingAction>>,
}
impl Actt {
/// Initialize a new helper object
///
/// All OIDS, managed by the controller must be provided
pub fn new(oids: &[&OID]) -> Self {
let mut pending_by_oid = HashMap::new();
for oid in oids {
pending_by_oid.insert((*oid).clone(), <_>::default());
}
Self {
pending_by_oid,
pending_by_uuid: <_>::default(),
}
}
/// Register additional OID
#[inline]
pub fn register(&mut self, oid: &OID) {
self.pending_by_oid.insert(oid.clone(), <_>::default());
}
/// Unregister OID on-the-flow
#[inline]
pub fn unregister(&mut self, oid: &OID) {
self.pending_by_oid.remove(oid);
}
/// Append new action to the helper
///
/// # Panics
///
/// Will panic if the internal mutexes are poisoned
pub fn append(&self, oid: &OID, uuid: Uuid) -> EResult<()> {
if let Some(actions) = self.pending_by_oid.get(oid) {
let mut a = actions.lock();
let mut a_uuid = self.pending_by_uuid.lock();
if let Entry::Vacant(o) = a.entry(uuid) {
o.insert(PendingAction::new());
a_uuid.insert(uuid, PendingAction::new());
Ok(())
} else {
Err(Error::core("duplicate action UUID"))
}
} else {
Err(Error::core(format!(
"{} is not in PENDING_ACTIONS map",
oid
)))
}
}
/// Remove action from the helper
///
/// If action was appended, it MUST be always removed either by RPC function (if failed to send
/// the action to handler) or by the handler
///
/// The handler checks the returned boolean and marks action canceled in case of false
///
/// After removing, the action no longer can be terminated
///
/// # Panics
///
/// Will panic if the internal mutexes are poisoned
pub fn remove(&self, oid: &OID, uuid: &Uuid) -> EResult<bool> {
if let Some(actions) = self.pending_by_oid.get(oid) {
let mut a = actions.lock();
let mut a_uuid = self.pending_by_uuid.lock();
if let Some(v) = a.remove(uuid) {
if let Some(v_u) = a_uuid.remove(uuid) {
Ok(v.is_active() && v_u.is_active())
} else {
Ok(v.is_active())
}
} else {
Err(Error::not_found("action not found in PENDING_ACTIONS map"))
}
} else {
Err(Error::core(format!(
"{} is not in PENDING_ACTIONS map",
oid
)))
}
}
/// The handler can check is the action active during its execution
///
/// # Panics
///
/// Will panic if the internal mutexes are poisoned
pub fn is_active(&self, oid: &OID, uuid: &Uuid) -> EResult<bool> {
if let Some(actions) = self.pending_by_oid.get(oid) {
let a = actions.lock();
let a_uuid = self.pending_by_uuid.lock();
if let Some(v) = a.get(uuid) {
if let Some(v_u) = a_uuid.get(uuid) {
Ok(v.is_active() && v_u.is_active())
} else {
Ok(v.is_active())
}
} else {
Err(Error::not_found("action not found in PENDING_ACTIONS map"))
}
} else {
Err(Error::core(format!(
"{} is not in PENDING_ACTIONS map",
oid
)))
}
}
/// Mark the action terminated by uuid
///
/// # Panics
///
/// Will panic if the internal mutexes are poisoned
pub fn mark_terminated(&self, uuid: &Uuid) -> EResult<()> {
let mut a_uuid = self.pending_by_uuid.lock();
if let Some(v_u) = a_uuid.get_mut(uuid) {
v_u.cancel();
Ok(())
} else {
Err(Error::not_found(format!("action {} not found", uuid)))
}
}
/// Mark all actions pending for OID terminated
///
/// # Panics
///
/// Will panic if the internal mutexes are poisoned
pub fn mark_killed(&self, oid: &OID) -> EResult<()> {
if let Some(actions) = self.pending_by_oid.get(oid) {
for (_, a) in actions.lock().iter_mut() {
a.cancel();
}
Ok(())
} else {
Err(Error::core(format!(
"{} is not in PENDING_ACTIONS map",
oid
)))
}
}
}
struct PendingAction {
active: bool,
}
impl PendingAction {
#[inline]
fn new() -> Self {
Self { active: true }
}
#[inline]
fn is_active(&self) -> bool {
self.active
}
#[inline]
fn cancel(&mut self) {
self.active = false;
}
}