lighthouse/beacon_node/beacon_chain/src/fork_choice_signal.rs
Michael Sproul 0866b739d0 Clippy 1.67 (#3916)
## Proposed Changes

Clippy 1.67.0 put us on blast for the size of some of our errors, most of them written by me ( 👀 ). This PR shrinks the size of `BeaconChainError` by dropping some extraneous info and boxing an inner error which should only occur infrequently anyway.

For the `AttestationSlashInfo` and `BlockSlashInfo` I opted to ignore the lint as they are always used in a `Result<A, Info>` where `A` is a similar size. This means they don't bloat the size of the `Result`, so it's a bit annoying for Clippy to report this as an issue.

I also chose to ignore `clippy::uninlined-format-args` because I think the benefit-to-churn ratio is too low. E.g. sometimes we have long identifiers in `format!` args and IMO the non-inlined form is easier to read:

```rust
// I prefer this...
format!(
    "{} did {} to {}",
    REALLY_LONG_CONSTANT_NAME,
    ANOTHER_REALLY_LONG_CONSTANT_NAME,
    regular_long_identifier_name
);
  
// To this
format!("{REALLY_LONG_CONSTANT_NAME} did {ANOTHER_REALLY_LONG_CONSTANT_NAME} to {regular_long_identifier_name}");
```

I tried generating an automatic diff with `cargo clippy --fix` but it came out at:

```
250 files changed, 1209 insertions(+), 1469 deletions(-)
```

Which seems like a bad idea when we'd have to back-merge it to `capella` and `eip4844` 😱
2023-01-27 09:48:42 +00:00

98 lines
2.9 KiB
Rust

//! Concurrency helpers for synchronising block proposal with fork choice.
//!
//! The transmitter provides a way for a thread runnning fork choice on a schedule to signal
//! to the receiver that fork choice has been updated for a given slot.
use crate::BeaconChainError;
use parking_lot::{Condvar, Mutex};
use std::sync::Arc;
use std::time::Duration;
use types::Slot;
/// Sender, for use by the per-slot task timer.
pub struct ForkChoiceSignalTx {
pair: Arc<(Mutex<Slot>, Condvar)>,
}
/// Receiver, for use by the beacon chain waiting on fork choice to complete.
pub struct ForkChoiceSignalRx {
pair: Arc<(Mutex<Slot>, Condvar)>,
}
pub enum ForkChoiceWaitResult {
/// Successfully reached a slot greater than or equal to the awaited slot.
Success(Slot),
/// Fork choice was updated to a lower slot, indicative of lag or processing delays.
Behind(Slot),
/// Timed out waiting for the fork choice update from the sender.
TimeOut,
}
impl ForkChoiceSignalTx {
pub fn new() -> Self {
let pair = Arc::new((Mutex::new(Slot::new(0)), Condvar::new()));
Self { pair }
}
pub fn get_receiver(&self) -> ForkChoiceSignalRx {
ForkChoiceSignalRx {
pair: self.pair.clone(),
}
}
/// Signal to the receiver that fork choice has been updated to `slot`.
///
/// Return an error if the provided `slot` is strictly less than any previously provided slot.
pub fn notify_fork_choice_complete(&self, slot: Slot) -> Result<(), BeaconChainError> {
let (lock, condvar) = &*self.pair;
let mut current_slot = lock.lock();
if slot < *current_slot {
return Err(BeaconChainError::ForkChoiceSignalOutOfOrder {
current: *current_slot,
latest: slot,
});
} else {
*current_slot = slot;
}
// We use `notify_all` because there may be multiple block proposals waiting simultaneously.
// Usually there'll be 0-1.
condvar.notify_all();
Ok(())
}
}
impl Default for ForkChoiceSignalTx {
fn default() -> Self {
Self::new()
}
}
impl ForkChoiceSignalRx {
pub fn wait_for_fork_choice(&self, slot: Slot, timeout: Duration) -> ForkChoiceWaitResult {
let (lock, condvar) = &*self.pair;
let mut current_slot = lock.lock();
// Wait for `current_slot >= slot`.
//
// Do not loop and wait, if we receive an update for the wrong slot then something is
// quite out of whack and we shouldn't waste more time waiting.
if *current_slot < slot {
let timeout_result = condvar.wait_for(&mut current_slot, timeout);
if timeout_result.timed_out() {
return ForkChoiceWaitResult::TimeOut;
}
}
if *current_slot >= slot {
ForkChoiceWaitResult::Success(*current_slot)
} else {
ForkChoiceWaitResult::Behind(*current_slot)
}
}
}