1use fuchsia_sync::Mutex;
18use std::borrow::Borrow;
19use std::cmp::Ordering;
20use std::collections::BTreeMap;
21use std::future::Future;
22
23const INSPECTOR_PROP_RESULT: &str = "result";
24const INSPECTOR_PROP_OVERRIDDEN: &str = "overridden";
25const INSPECTOR_NODE_VOTES: &str = "votes";
26
27pub trait InspectValue {
32 fn record_inspect(&self, node: &fuchsia_inspect::Node, key: &str);
33}
34
35impl InspectValue for u32 {
36 fn record_inspect(&self, node: &fuchsia_inspect::Node, key: &str) {
37 node.record_uint(key, u64::from(*self));
38 }
39}
40
41impl InspectValue for u64 {
42 fn record_inspect(&self, node: &fuchsia_inspect::Node, key: &str) {
43 node.record_uint(key, *self);
44 }
45}
46
47impl InspectValue for i64 {
48 fn record_inspect(&self, node: &fuchsia_inspect::Node, key: &str) {
49 node.record_int(key, *self);
50 }
51}
52
53impl InspectValue for bool {
54 fn record_inspect(&self, node: &fuchsia_inspect::Node, key: &str) {
55 node.record_bool(key, *self);
56 }
57}
58
59#[derive(Debug)]
66pub struct Election<K, V> {
67 cmp: fn(&V, &V) -> Ordering,
69 votes: Mutex<BTreeMap<K, V>>,
71 override_voter: Option<K>,
73}
74
75impl<K: Ord + Clone, V: Copy> Election<K, V> {
76 pub fn new(cmp: fn(&V, &V) -> Ordering) -> Self {
79 Self { cmp, votes: Mutex::new(BTreeMap::new()), override_voter: None }
80 }
81
82 pub fn with_override_voter(mut self, override_voter: K) -> Self {
84 self.override_voter = Some(override_voter);
85 self
86 }
87
88 fn is_overridden_locked(&self, votes: &BTreeMap<K, V>) -> bool {
89 self.override_voter.as_ref().is_some_and(|voter| votes.contains_key(voter))
90 }
91
92 fn result_locked(&self, votes: &BTreeMap<K, V>) -> Option<V> {
93 self.override_voter
94 .as_ref()
95 .and_then(|voter| votes.get(voter).copied())
96 .or_else(|| votes.values().copied().max_by(|a, b| (self.cmp)(a, b)))
97 }
98
99 fn swap(&self, voter: K, vote: Option<V>) -> (Option<V>, Option<V>, Option<V>) {
102 let mut votes = self.votes.lock();
103 let previous_result = self.result_locked(&votes);
104 let previous_vote = match vote {
105 Some(vote) => votes.insert(voter, vote),
106 None => votes.remove(&voter),
107 };
108 (previous_vote, previous_result, self.result_locked(&votes))
109 }
110
111 fn snapshot(&self) -> (Option<V>, bool, Vec<(K, V)>) {
114 let votes = self.votes.lock();
115 let copy = votes.iter().map(|(voter, vote)| (voter.clone(), *vote)).collect();
116 (self.result_locked(&votes), self.is_overridden_locked(&votes), copy)
117 }
118
119 pub fn is_overridden(&self) -> bool {
121 self.is_overridden_locked(&self.votes.lock())
122 }
123
124 pub fn result(&self) -> Option<V> {
126 self.result_locked(&self.votes.lock())
127 }
128
129 pub fn vote<Q>(&self, voter: &Q) -> Option<V>
131 where
132 K: Borrow<Q>,
133 Q: Ord + ?Sized,
134 {
135 self.votes.lock().get(voter).copied()
136 }
137
138 pub fn cast(&self, voter: K, vote: V) -> Option<V> {
140 self.votes.lock().insert(voter, vote)
141 }
142
143 pub fn withdraw<Q>(&self, voter: &Q) -> Option<V>
145 where
146 K: Borrow<Q>,
147 Q: Ord + ?Sized,
148 {
149 self.votes.lock().remove(voter)
150 }
151
152 pub fn set_vote(&self, voter: K, vote: Option<V>) -> Option<V> {
155 self.swap(voter, vote).0
156 }
157
158 pub async fn try_cast<E, F, Fut>(&self, voter: K, vote: V, apply: F) -> Result<bool, E>
161 where
162 V: PartialEq,
163 F: FnOnce(Option<V>) -> Fut,
164 Fut: Future<Output = Result<(), E>>,
165 {
166 self.try_set_vote(voter, Some(vote), apply).await
167 }
168
169 pub async fn try_withdraw<E, F, Fut>(&self, voter: K, apply: F) -> Result<bool, E>
172 where
173 V: PartialEq,
174 F: FnOnce(Option<V>) -> Fut,
175 Fut: Future<Output = Result<(), E>>,
176 {
177 self.try_set_vote(voter, None, apply).await
178 }
179
180 pub async fn try_set_vote<E, F, Fut>(
190 &self,
191 voter: K,
192 vote: Option<V>,
193 apply: F,
194 ) -> Result<bool, E>
195 where
196 V: PartialEq,
197 F: FnOnce(Option<V>) -> Fut,
198 Fut: Future<Output = Result<(), E>>,
199 {
200 let (previous_vote, previous_result, result) = self.swap(voter.clone(), vote);
201 if result == previous_result {
202 return Ok(false);
203 }
204 let rollback = Rollback { election: self, voter: Some(voter), previous_vote };
205 apply(result).await?;
206 rollback.defuse();
207 Ok(true)
208 }
209}
210
211struct Rollback<'a, K: Ord + Clone, V: Copy> {
213 election: &'a Election<K, V>,
214 voter: Option<K>,
216 previous_vote: Option<V>,
217}
218
219impl<K: Ord + Clone, V: Copy> Rollback<'_, K, V> {
220 fn defuse(mut self) {
222 self.voter = None;
223 }
224}
225
226impl<K: Ord + Clone, V: Copy> Drop for Rollback<'_, K, V> {
227 fn drop(&mut self) {
228 if let Some(voter) = self.voter.take() {
229 self.election.swap(voter, self.previous_vote);
230 }
231 }
232}
233
234impl<K: Ord + Clone + AsRef<str>, V: Copy> Election<K, V> {
235 pub fn record_inspect_with(
238 &self,
239 parent: &fuchsia_inspect::Node,
240 name: &str,
241 record: impl Fn(&V, &fuchsia_inspect::Node, &str),
242 ) {
243 let (result, overridden, votes) = self.snapshot();
246 parent.record_child(name, |node| {
247 if let Some(result) = result {
248 record(&result, node, INSPECTOR_PROP_RESULT);
249 }
250 node.record_bool(INSPECTOR_PROP_OVERRIDDEN, overridden);
251 node.record_child(INSPECTOR_NODE_VOTES, |votes_node| {
252 for (voter, vote) in &votes {
253 record(vote, votes_node, voter.as_ref());
254 }
255 });
256 });
257 }
258}
259
260impl<K: Ord + Clone + AsRef<str>, V: Copy + InspectValue> Election<K, V> {
261 pub fn record_inspect(&self, parent: &fuchsia_inspect::Node, name: &str) {
263 self.record_inspect_with(parent, name, InspectValue::record_inspect);
264 }
265}
266
267impl<K: Ord + Clone, V: Copy + Ord> Election<K, V> {
268 pub fn min() -> Self {
270 Self::new(|a, b| b.cmp(a))
271 }
272
273 pub fn max() -> Self {
275 Self::new(Ord::cmp)
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282 use diagnostics_assertions::assert_data_tree;
283 use std::pin::pin;
284 use std::task::{Context, Poll, Waker};
285
286 #[fuchsia::test]
289 fn test_election_min_with_override_voter() {
290 const VOTER_A: u64 = 1;
291 const VOTER_B: u64 = 2;
292 const OVERRIDE_VOTER: u64 = 99;
293 const HIGH_LIMIT_UA: u32 = 1_500_000;
294 const MID_LIMIT_UA: u32 = 500_000;
295 const LOW_LIMIT_UA: u32 = 300_000;
296 const OVERRIDE_LIMIT_UA: u32 = 2_000_000;
297
298 let e = Election::min().with_override_voter(OVERRIDE_VOTER);
299 assert_eq!(e.result(), None);
300
301 assert_eq!(e.cast(VOTER_A, HIGH_LIMIT_UA), None);
302 assert_eq!(e.result(), Some(HIGH_LIMIT_UA));
303 assert_eq!(e.vote(&VOTER_A), Some(HIGH_LIMIT_UA));
304
305 e.cast(VOTER_B, MID_LIMIT_UA);
306 assert_eq!(e.result(), Some(MID_LIMIT_UA));
307
308 assert_eq!(e.cast(VOTER_A, LOW_LIMIT_UA), Some(HIGH_LIMIT_UA));
309 assert_eq!(e.result(), Some(LOW_LIMIT_UA));
310
311 e.cast(OVERRIDE_VOTER, OVERRIDE_LIMIT_UA);
314 assert!(e.is_overridden());
315 assert_eq!(e.result(), Some(OVERRIDE_LIMIT_UA));
316
317 assert_eq!(e.withdraw(&OVERRIDE_VOTER), Some(OVERRIDE_LIMIT_UA));
318 assert!(!e.is_overridden());
319 assert_eq!(e.vote(&OVERRIDE_VOTER), None);
320 assert_eq!(e.result(), Some(LOW_LIMIT_UA));
321
322 assert_eq!(e.set_vote(VOTER_A, None), Some(LOW_LIMIT_UA));
323 assert_eq!(e.result(), Some(MID_LIMIT_UA));
324 assert_eq!(e.set_vote(VOTER_A, Some(HIGH_LIMIT_UA)), None);
325 assert_eq!(e.result(), Some(MID_LIMIT_UA));
326
327 assert_eq!(e.withdraw(&VOTER_A), Some(HIGH_LIMIT_UA));
329 assert_eq!(e.withdraw(&VOTER_A), None);
330
331 assert_eq!(e.withdraw(&VOTER_B), Some(MID_LIMIT_UA));
332 assert_eq!(e.result(), None);
333 }
334
335 #[fuchsia::test]
338 async fn test_election_max_with_override_voter_and_record_inspect() {
339 let e = Election::max().with_override_voter("debug");
340 e.cast("vreg", false);
341 e.cast("controller", true);
342 assert_eq!(e.result(), Some(true));
343
344 e.cast("debug", false);
346 assert_eq!(e.result(), Some(false));
347
348 let inspector = fuchsia_inspect::Inspector::default();
349 e.record_inspect(inspector.root(), "otg_enabled");
350 assert_data_tree!(inspector, root: {
351 otg_enabled: {
352 result: false,
353 overridden: true,
354 votes: {
355 vreg: false,
356 controller: true,
357 debug: false,
358 },
359 },
360 });
361
362 e.withdraw("debug");
363 assert!(!e.is_overridden());
364 assert_eq!(e.result(), Some(true));
365
366 e.cast("controller", false);
367 assert_eq!(e.result(), Some(false));
368 }
369
370 #[fuchsia::test]
374 async fn test_election_custom_comparator_string_voters_and_record_inspect_with() {
375 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
376 enum Mode {
377 Charging,
378 Passthrough,
379 Discharging,
380 }
381
382 fn rank(m: &Mode) -> u8 {
383 match m {
384 Mode::Charging => 0,
385 Mode::Passthrough => 1,
386 Mode::Discharging => 2,
387 }
388 }
389
390 let debug = String::from("debug");
391 let msc = String::from("msc");
392 let thermal = String::from("thermal");
393 let defender = String::from("defender");
394
395 let e = Election::new(|a, b| rank(a).cmp(&rank(b))).with_override_voter(debug.clone());
396 e.cast(msc.clone(), Mode::Charging);
397 assert_eq!(e.vote("msc"), Some(Mode::Charging));
398 e.cast(thermal.clone(), Mode::Passthrough);
399 assert_eq!(e.result(), Some(Mode::Passthrough));
400
401 e.cast(defender.clone(), Mode::Discharging);
402 assert_eq!(e.result(), Some(Mode::Discharging));
403
404 let inspector = fuchsia_inspect::Inspector::default();
405 e.record_inspect_with(inspector.root(), "operating_mode", |mode, node, key| {
406 node.record_string(key, format!("{mode:?}"));
407 });
408 assert_data_tree!(inspector, root: {
409 operating_mode: {
410 result: "Discharging",
411 overridden: false,
412 votes: {
413 defender: "Discharging",
414 msc: "Charging",
415 thermal: "Passthrough",
416 },
417 },
418 });
419
420 e.cast(debug.clone(), Mode::Charging);
422 assert_eq!(e.result(), Some(Mode::Charging));
423
424 e.withdraw(&debug);
425 assert_eq!(e.result(), Some(Mode::Discharging));
426
427 e.withdraw(&defender);
428 assert_eq!(e.result(), Some(Mode::Passthrough));
429 }
430
431 #[fuchsia::test]
434 async fn test_election_try_cast_and_try_withdraw_rollback() {
435 const VOTER_A: u8 = 1;
436 const VOTER_B: u8 = 2;
437 const INITIAL_LIMIT_UA: u32 = 100;
438 const LOWER_LIMIT_UA: u32 = 50;
439
440 let e = Election::min();
441
442 let res = e
444 .try_cast(VOTER_A, INITIAL_LIMIT_UA, |result| async move {
445 assert_eq!(result, Some(INITIAL_LIMIT_UA));
446 Err("io")
447 })
448 .await;
449 assert_eq!(res, Err("io"));
450 assert_eq!(e.vote(&VOTER_A), None);
451 assert_eq!(e.result(), None);
452
453 let res: Result<bool, &'static str> = e
456 .try_cast(VOTER_A, INITIAL_LIMIT_UA, |result| async move {
457 assert_eq!(result, Some(INITIAL_LIMIT_UA));
458 Ok(())
459 })
460 .await;
461 assert_eq!(res, Ok(true));
462 assert_eq!(e.vote(&VOTER_A), Some(INITIAL_LIMIT_UA));
463
464 let res: Result<bool, &'static str> = e
465 .try_cast(VOTER_A, INITIAL_LIMIT_UA, |_result| async {
466 panic!("apply should not run when the vote is unchanged");
467 })
468 .await;
469 assert_eq!(res, Ok(false));
470
471 let res = e
473 .try_cast(VOTER_A, LOWER_LIMIT_UA, |result| async move {
474 assert_eq!(result, Some(LOWER_LIMIT_UA));
475 Err("io")
476 })
477 .await;
478 assert_eq!(res, Err("io"));
479 assert_eq!(e.vote(&VOTER_A), Some(INITIAL_LIMIT_UA));
480
481 let res = e
483 .try_withdraw(VOTER_A, |result| async move {
484 assert_eq!(result, None);
485 Err("io")
486 })
487 .await;
488 assert_eq!(res, Err("io"));
489 assert_eq!(e.vote(&VOTER_A), Some(INITIAL_LIMIT_UA));
490
491 let res: Result<bool, &'static str> = e
493 .try_cast(VOTER_B, LOWER_LIMIT_UA, |result| async move {
494 assert_eq!(result, Some(LOWER_LIMIT_UA));
495 Ok(())
496 })
497 .await;
498 assert_eq!(res, Ok(true));
499
500 let res: Result<bool, &'static str> = e
502 .try_withdraw(VOTER_A, |_result| async {
503 panic!("apply should not run when the result is unchanged");
504 })
505 .await;
506 assert_eq!(res, Ok(false));
507 assert_eq!(e.vote(&VOTER_A), None);
508
509 let res: Result<bool, &'static str> = e
511 .try_withdraw(VOTER_B, |result| async move {
512 assert_eq!(result, None);
513 Ok(())
514 })
515 .await;
516 assert_eq!(res, Ok(true));
517 let res: Result<bool, &'static str> = e
518 .try_withdraw(VOTER_B, |_result| async {
519 panic!("apply should not run when the voter has no vote");
520 })
521 .await;
522 assert_eq!(res, Ok(false));
523 }
524
525 #[fuchsia::test]
528 async fn test_election_record_inspect_with_allows_reentrant_record() {
529 let e = Election::min();
530 e.cast("a", 2u32);
531 e.cast("b", 1u32);
532
533 let inspector = fuchsia_inspect::Inspector::default();
534 e.record_inspect_with(inspector.root(), "limit", |vote, node, key| {
535 assert_eq!(e.result(), Some(1));
537 node.record_uint(key, u64::from(*vote));
538 });
539 assert_data_tree!(inspector, root: {
540 limit: {
541 result: 1u64,
542 overridden: false,
543 votes: {
544 a: 2u64,
545 b: 1u64,
546 },
547 },
548 });
549 }
550
551 #[fuchsia::test]
554 fn test_election_try_cast_restores_vote_when_cancelled() {
555 const VOTER: u8 = 1;
556 const INITIAL_LIMIT_UA: u32 = 100;
557 const LOWER_LIMIT_UA: u32 = 50;
558
559 let e = Election::min();
560 e.cast(VOTER, INITIAL_LIMIT_UA);
561 {
562 let mut update = pin!(e.try_cast(VOTER, LOWER_LIMIT_UA, |_result| {
563 std::future::pending::<Result<(), ()>>()
564 }));
565 let mut cx = Context::from_waker(Waker::noop());
566 assert_eq!(update.as_mut().poll(&mut cx), Poll::Pending);
567 assert_eq!(e.result(), Some(LOWER_LIMIT_UA));
568 }
569 assert_eq!(e.vote(&VOTER), Some(INITIAL_LIMIT_UA));
570 assert_eq!(e.result(), Some(INITIAL_LIMIT_UA));
571 }
572}