blob: 655aacff39b09a5ff2fe8f6ec00331a80948e86d [file] [view]
# election
`election` provides `Election<K, V>`, which arbitrates a single setting across
multiple voters and elects the winning value (the result).
It is FIDL-agnostic and shared by components that combine requests from several
clients into one hardware setting, such as charger drivers (arbitrating
`Controller`, `Debug`, and `Vreg` votes) and `battery-manager` (arbitrating
votes from charge governors).
Linux charger drivers call the same object a votable (Qualcomm `pmic-voter`) or
an election (Google `gvotable`).
## Concepts
- **Voter (`K`)**: identifies who cast a vote, such as an enum of client types
or a governor's name (`String`). Each voter has at most one vote.
- **Vote (`V`)**: the value a voter requests. A voter casts a vote with `cast`
and withdraws it with `withdraw` (`set_vote` takes `Some` or `None`).
- **Result**: the winning vote across all active voters, or `None` if no votes
are active. Callers apply their own default when it is `None`.
- **Comparator**: ranks active votes; the vote ranked `Ordering::Greater` wins.
- `Election::min()`: the lowest vote wins (for example, current or voltage
ceilings, where every voter's limit must be honored).
- `Election::max()`: the highest vote wins.
- `Election::new(cmp)`: a custom precedence, for value types without `Ord`
(such as FIDL enums).
- **Override voter** (optional): `with_override_voter(k)` designates a voter
whose active vote wins over all other voters regardless of the comparator (for
example, a debug override).
## Usage
```rust
use election::Election;
let fcc_ua = Election::<&str, u32>::min().with_override_voter("debug");
fcc_ua.cast("msc", 2_000_000);
fcc_ua.cast("thermal", 1_000_000);
assert_eq!(fcc_ua.result(), Some(1_000_000));
// The override voter wins even under `min()`.
fcc_ua.cast("debug", 3_000_000);
assert_eq!(fcc_ua.result(), Some(3_000_000));
// Withdrawing a vote restores the election across the remaining voters.
fcc_ua.withdraw("debug");
assert_eq!(fcc_ua.result(), Some(1_000_000));
```
### Applying single-field changes
`try_cast` and `try_withdraw` (or `try_set_vote` with `Some` or `None`) update a
voter's vote and, only if the result changes, run an async `apply` closure (for
example, a register write). If `apply` fails, or the returned future is dropped
before `apply` completes, the voter's previous vote is restored so that the
election matches what was last applied and the request can be retried:
```rust
let changed = fcc_ua
.try_cast("msc", 1_500_000, |result| async move {
program_fcc(result.unwrap_or(DEFAULT_FCC_UA)).await
})
.await?;
```
They return `Ok(true)` if the result changed and was applied, or `Ok(false)` if
the result was unchanged (and `apply` did not run). A failed or cancelled
`apply` may still have partially taken effect.
### Multi-field transactions
When one request updates several `Election`s that must be applied together (for
example, a single driver call carrying every changed field), use the
lower-level APIs and roll back on failure:
1. Snapshot each voter's previous votes with `vote` (or keep the value returned
by `set_vote`).
2. Cast or withdraw the new votes with `set_vote`.
3. Read each `result` and apply them together.
4. On failure, restore the previous votes with `set_vote`.
`Election` holds its internal lock only for short map operations, never across
`.await` points, so callers must serialize multi-field transactions themselves
(for example, behind an async mutex).
### Inspect
`record_inspect(parent, name)` records a node for the election:
```
<name>:
result: <winning vote, omitted when no votes are active>
overridden: <whether the override voter has an active vote>
votes:
<voter>: <vote>
```
Values implement `InspectValue` (provided for `u32`, `u64`, `i64`, and `bool`).
For value types defined in other crates, which cannot implement `InspectValue`,
use `record_inspect_with` and pass a closure that records each value:
```rust
mode.record_inspect_with(node, "operating_mode", |mode, node, key| {
node.record_string(key, format!("{mode:?}"));
});
```
Voter keys must implement `AsRef<str>` to be recorded.
## Testing
```
fx test election-tests
```