eva_sdk/
actt.rs

1use eva_common::prelude::*;
2use parking_lot::Mutex;
3use std::collections::hash_map::Entry;
4use std::collections::HashMap;
5use uuid::Uuid;
6
7/// Action termination helper for EVA ICS controller services
8pub struct Actt {
9    pending_by_oid: HashMap<OID, Mutex<HashMap<Uuid, PendingAction>>>,
10    pending_by_uuid: Mutex<HashMap<Uuid, PendingAction>>,
11}
12
13impl Actt {
14    /// Initialize a new helper object
15    ///
16    /// All OIDS, managed by the controller must be provided
17    pub fn new(oids: &[&OID]) -> Self {
18        let mut pending_by_oid = HashMap::new();
19        for oid in oids {
20            pending_by_oid.insert((*oid).clone(), <_>::default());
21        }
22        Self {
23            pending_by_oid,
24            pending_by_uuid: <_>::default(),
25        }
26    }
27    /// Register additional OID
28    #[inline]
29    pub fn register(&mut self, oid: &OID) {
30        self.pending_by_oid.insert(oid.clone(), <_>::default());
31    }
32    /// Unregister OID on-the-flow
33    #[inline]
34    pub fn unregister(&mut self, oid: &OID) {
35        self.pending_by_oid.remove(oid);
36    }
37    /// Append new action to the helper
38    ///
39    /// # Panics
40    ///
41    /// Will panic if the internal mutexes are poisoned
42    pub fn append(&self, oid: &OID, uuid: Uuid) -> EResult<()> {
43        if let Some(actions) = self.pending_by_oid.get(oid) {
44            let mut a = actions.lock();
45            let mut a_uuid = self.pending_by_uuid.lock();
46            if let Entry::Vacant(o) = a.entry(uuid) {
47                o.insert(PendingAction::new());
48                a_uuid.insert(uuid, PendingAction::new());
49                Ok(())
50            } else {
51                Err(Error::core("duplicate action UUID"))
52            }
53        } else {
54            Err(Error::core(format!(
55                "{} is not in PENDING_ACTIONS map",
56                oid
57            )))
58        }
59    }
60    /// Remove action from the helper
61    ///
62    /// If action was appended, it MUST be always removed either by RPC function (if failed to send
63    /// the action to handler) or by the handler
64    ///
65    /// The handler checks the returned boolean and marks action canceled in case of false
66    ///
67    /// After removing, the action no longer can be terminated
68    ///
69    /// # Panics
70    ///
71    /// Will panic if the internal mutexes are poisoned
72    pub fn remove(&self, oid: &OID, uuid: &Uuid) -> EResult<bool> {
73        if let Some(actions) = self.pending_by_oid.get(oid) {
74            let mut a = actions.lock();
75            let mut a_uuid = self.pending_by_uuid.lock();
76            if let Some(v) = a.remove(uuid) {
77                if let Some(v_u) = a_uuid.remove(uuid) {
78                    Ok(v.is_active() && v_u.is_active())
79                } else {
80                    Ok(v.is_active())
81                }
82            } else {
83                Err(Error::not_found("action not found in PENDING_ACTIONS map"))
84            }
85        } else {
86            Err(Error::core(format!(
87                "{} is not in PENDING_ACTIONS map",
88                oid
89            )))
90        }
91    }
92    /// The handler can check is the action active during its execution
93    ///
94    /// # Panics
95    ///
96    /// Will panic if the internal mutexes are poisoned
97    pub fn is_active(&self, oid: &OID, uuid: &Uuid) -> EResult<bool> {
98        if let Some(actions) = self.pending_by_oid.get(oid) {
99            let a = actions.lock();
100            let a_uuid = self.pending_by_uuid.lock();
101            if let Some(v) = a.get(uuid) {
102                if let Some(v_u) = a_uuid.get(uuid) {
103                    Ok(v.is_active() && v_u.is_active())
104                } else {
105                    Ok(v.is_active())
106                }
107            } else {
108                Err(Error::not_found("action not found in PENDING_ACTIONS map"))
109            }
110        } else {
111            Err(Error::core(format!(
112                "{} is not in PENDING_ACTIONS map",
113                oid
114            )))
115        }
116    }
117    /// Mark the action terminated by uuid
118    ///
119    /// # Panics
120    ///
121    /// Will panic if the internal mutexes are poisoned
122    pub fn mark_terminated(&self, uuid: &Uuid) -> EResult<()> {
123        let mut a_uuid = self.pending_by_uuid.lock();
124        if let Some(v_u) = a_uuid.get_mut(uuid) {
125            v_u.cancel();
126            Ok(())
127        } else {
128            Err(Error::not_found(format!("action {} not found", uuid)))
129        }
130    }
131    /// Mark all actions pending for OID terminated
132    ///
133    /// # Panics
134    ///
135    /// Will panic if the internal mutexes are poisoned
136    pub fn mark_killed(&self, oid: &OID) -> EResult<()> {
137        if let Some(actions) = self.pending_by_oid.get(oid) {
138            for a in actions.lock().values_mut() {
139                a.cancel();
140            }
141            Ok(())
142        } else {
143            Err(Error::core(format!(
144                "{} is not in PENDING_ACTIONS map",
145                oid
146            )))
147        }
148    }
149}
150
151struct PendingAction {
152    active: bool,
153}
154
155impl PendingAction {
156    #[inline]
157    fn new() -> Self {
158        Self { active: true }
159    }
160    #[inline]
161    fn is_active(&self) -> bool {
162        self.active
163    }
164    #[inline]
165    fn cancel(&mut self) {
166        self.active = false;
167    }
168}