Simplify `Sleeper` init in sync `lightning-background-processor`
What changed, and why it matters
This commit is a straightforward internal code cleanup in the Lightning Dev Kit Rust library. It replaces several hand-written helper functions for waiting on 2, 3, or 4 background tasks with a single helper that accepts any number of tasks using Rust's standard iterator features. There is no indication this fixes a security bug or changes user-facing behavior.
No security action required. Treat as normal refactoring/code-quality improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors Sleeper construction in lightning-background-processor and lightning/src/util/wakers.rs. It removes from_two_futures, from_three_futures, and from_four_futures, replacing them with a generic from_futures<I: IntoIterator<Item = Future>>. The background processor now builds an iterator over the always-present futures and chains optional onion_messenger and liquidity_manager futures via Option as iterator. Test code is updated to use the new API. The change is behavior-preserving and simplifies adding another optional future later.
Changed components
lightning-background-processor/src/lib.rslightning/src/util/wakers.rsInspect captured patch +23 / −60
diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs
index aae738a..36b563f 100644
--- a/lightning-background-processor/src/lib.rs
+++ b/lightning-background-processor/src/lib.rs
@@ -1635,28 +1635,17 @@ impl BackgroundProcessor {
log_trace!(logger, "Terminating background processor.");
break;
}
- let sleeper = match (onion_messenger.as_ref(), liquidity_manager.as_ref()) {
- (Some(om), Some(lm)) => Sleeper::from_four_futures(
- &channel_manager.get_cm().get_event_or_persistence_needed_future(),
- &chain_monitor.get_update_future(),
- &om.get_om().get_update_future(),
- &lm.get_lm().get_pending_msgs_or_needs_persist_future(),
- ),
- (Some(om), None) => Sleeper::from_three_futures(
- &channel_manager.get_cm().get_event_or_persistence_needed_future(),
- &chain_monitor.get_update_future(),
- &om.get_om().get_update_future(),
- ),
- (None, Some(lm)) => Sleeper::from_three_futures(
- &channel_manager.get_cm().get_event_or_persistence_needed_future(),
- &chain_monitor.get_update_future(),
- &lm.get_lm().get_pending_msgs_or_needs_persist_future(),
- ),
- (None, None) => Sleeper::from_two_futures(
- &channel_manager.get_cm().get_event_or_persistence_needed_future(),
- &chain_monitor.get_update_future(),
- ),
- };
+ let om_fut = onion_messenger.as_ref().map(|om| om.get_om().get_update_future());
+ let lm_fut = liquidity_manager
+ .as_ref()
+ .map(|lm| lm.get_lm().get_pending_msgs_or_needs_persist_future());
+ let always_futures = [
+ channel_manager.get_cm().get_event_or_persistence_needed_future(),
+ chain_monitor.get_update_future(),
+ ];
+ let futures = always_futures.into_iter().chain(om_fut).chain(lm_fut);
+ let sleeper = Sleeper::from_futures(futures);
+
let batch_delay = if channel_manager.get_cm().needs_pending_htlc_processing() {
batch_delay.get()
} else {
diff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index a84d909..17edadf 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -253,37 +253,13 @@ impl Sleeper {
pub fn from_single_future(future: &Future) -> Self {
Self { notifiers: vec![Arc::clone(&future.state)] }
}
- /// Constructs a new sleeper from two futures, allowing blocking on both at once.
- pub fn from_two_futures(fut_a: &Future, fut_b: &Future) -> Self {
- Self { notifiers: vec![Arc::clone(&fut_a.state), Arc::clone(&fut_b.state)] }
- }
- /// Constructs a new sleeper from three futures, allowing blocking on all three at once.
- ///
- // Note that this is the common case - a ChannelManager, a ChainMonitor, and an
- // OnionMessenger.
- pub fn from_three_futures(fut_a: &Future, fut_b: &Future, fut_c: &Future) -> Self {
- let notifiers =
- vec![Arc::clone(&fut_a.state), Arc::clone(&fut_b.state), Arc::clone(&fut_c.state)];
- Self { notifiers }
- }
- /// Constructs a new sleeper from four futures, allowing blocking on all four at once.
- ///
- // Note that this is another common case - a ChannelManager, a ChainMonitor, an
- // OnionMessenger, and a LiquidityManager.
- pub fn from_four_futures(
- fut_a: &Future, fut_b: &Future, fut_c: &Future, fut_d: &Future,
- ) -> Self {
- let notifiers = vec![
- Arc::clone(&fut_a.state),
- Arc::clone(&fut_b.state),
- Arc::clone(&fut_c.state),
- Arc::clone(&fut_d.state),
- ];
- Self { notifiers }
+ /// Constructs an iterator of futures, allowing blocking on all at once.
+ pub fn from_futures<I: IntoIterator<Item = Future>>(futures: I) -> Self {
+ Self { notifiers: futures.into_iter().map(|f| Arc::clone(&f.state)).collect() }
}
/// Constructs a new sleeper on many futures, allowing blocking on all at once.
pub fn new(futures: Vec<Future>) -> Self {
- Self { notifiers: futures.into_iter().map(|f| Arc::clone(&f.state)).collect() }
+ Self::from_futures(futures)
}
/// Prepares to go into a wait loop body, creating a condition variable which we can block on
/// and an `Arc<Mutex<Option<_>>>` which gets set to the waking `Future`'s state prior to the
@@ -506,15 +482,13 @@ mod tests {
// Wait on the other thread to finish its sleep, note that the leak only happened if we
// actually have to sleep here, not if we immediately return.
- Sleeper::from_two_futures(&future_a, &future_b).wait();
+ Sleeper::from_futures([future_a, future_b]).wait();
join_handle.join().unwrap();
// then drop the notifiers and make sure the future states are gone.
mem::drop(notifier_a);
mem::drop(notifier_b);
- mem::drop(future_a);
- mem::drop(future_b);
assert!(future_state_a.upgrade().is_none() && future_state_b.upgrade().is_none());
}
@@ -736,18 +710,18 @@ mod tests {
// Set both notifiers as woken without sleeping yet.
notifier_a.notify();
notifier_b.notify();
- Sleeper::from_two_futures(¬ifier_a.get_future(), ¬ifier_b.get_future()).wait();
+ Sleeper::from_futures([notifier_a.get_future(), notifier_b.get_future()]).wait();
// One future has woken us up, but the other should still have a pending notification.
- Sleeper::from_two_futures(¬ifier_a.get_future(), ¬ifier_b.get_future()).wait();
+ Sleeper::from_futures([notifier_a.get_future(), notifier_b.get_future()]).wait();
// However once we've slept twice, we should no longer have any pending notifications
- assert!(!Sleeper::from_two_futures(¬ifier_a.get_future(), ¬ifier_b.get_future())
+ assert!(!Sleeper::from_futures([notifier_a.get_future(), notifier_b.get_future()])
.wait_timeout(Duration::from_millis(10)));
// Test ordering somewhat more.
notifier_a.notify();
- Sleeper::from_two_futures(¬ifier_a.get_future(), ¬ifier_b.get_future()).wait();
+ Sleeper::from_futures([notifier_a.get_future(), notifier_b.get_future()]).wait();
}
#[test]
@@ -765,7 +739,7 @@ mod tests {
// After sleeping one future (not guaranteed which one, however) will have its notification
// bit cleared.
- Sleeper::from_two_futures(¬ifier_a.get_future(), ¬ifier_b.get_future()).wait();
+ Sleeper::from_futures([notifier_a.get_future(), notifier_b.get_future()]).wait();
// By registering a callback on the futures for both notifiers, one will complete
// immediately, but one will remain tied to the notifier, and will complete once the
@@ -788,8 +762,8 @@ mod tests {
notifier_b.notify();
assert!(callback_a.load(Ordering::SeqCst) && callback_b.load(Ordering::SeqCst));
- Sleeper::from_two_futures(¬ifier_a.get_future(), ¬ifier_b.get_future()).wait();
- assert!(!Sleeper::from_two_futures(¬ifier_a.get_future(), ¬ifier_b.get_future())
+ Sleeper::from_futures([notifier_a.get_future(), notifier_b.get_future()]).wait();
+ assert!(!Sleeper::from_futures([notifier_a.get_future(), notifier_b.get_future()])
.wait_timeout(Duration::from_millis(10)));
}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.