1 // This file is Copyright its original authors, visible in version control
4 // This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5 // or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7 // You may not use this file except in accordance with one or both of these
10 //! Utilities which allow users to block on some future notification from LDK. These are
11 //! specifically used by [`ChannelManager`] to allow waiting until the [`ChannelManager`] needs to
14 //! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
17 use core::time::Duration;
18 use sync::{Condvar, Mutex};
20 #[cfg(any(test, feature = "std"))]
21 use std::time::Instant;
23 /// Used to signal to the ChannelManager persister that the manager needs to be re-persisted to
24 /// disk/backups, through `await_persistable_update_timeout` and `await_persistable_update`.
25 pub(crate) struct PersistenceNotifier {
26 /// Users won't access the persistence_lock directly, but rather wait on its bool using
27 /// `wait_timeout` and `wait`.
28 persistence_lock: (Mutex<bool>, Condvar),
31 impl PersistenceNotifier {
32 pub(crate) fn new() -> Self {
34 persistence_lock: (Mutex::new(false), Condvar::new()),
38 pub(crate) fn wait(&self) {
40 let &(ref mtx, ref cvar) = &self.persistence_lock;
41 let mut guard = mtx.lock().unwrap();
46 guard = cvar.wait(guard).unwrap();
55 #[cfg(any(test, feature = "std"))]
56 pub(crate) fn wait_timeout(&self, max_wait: Duration) -> bool {
57 let current_time = Instant::now();
59 let &(ref mtx, ref cvar) = &self.persistence_lock;
60 let mut guard = mtx.lock().unwrap();
65 guard = cvar.wait_timeout(guard, max_wait).unwrap().0;
66 // Due to spurious wakeups that can happen on `wait_timeout`, here we need to check if the
67 // desired wait time has actually passed, and if not then restart the loop with a reduced wait
68 // time. Note that this logic can be highly simplified through the use of
69 // `Condvar::wait_while` and `Condvar::wait_timeout_while`, if and when our MSRV is raised to
71 let elapsed = current_time.elapsed();
73 if result || elapsed >= max_wait {
77 match max_wait.checked_sub(elapsed) {
78 None => return result,
84 /// Wake waiters, tracking that persistence needs to occur.
85 pub(crate) fn notify(&self) {
86 let &(ref persist_mtx, ref cnd) = &self.persistence_lock;
87 let mut persistence_lock = persist_mtx.lock().unwrap();
88 *persistence_lock = true;
89 mem::drop(persistence_lock);
93 #[cfg(any(test, feature = "_test_utils"))]
94 pub fn needs_persist(&self) -> bool {
95 let &(ref mtx, _) = &self.persistence_lock;
96 let guard = mtx.lock().unwrap();
103 #[cfg(feature = "std")]
105 fn test_wait_timeout() {
108 use core::sync::atomic::{AtomicBool, Ordering};
111 let persistence_notifier = Arc::new(PersistenceNotifier::new());
112 let thread_notifier = Arc::clone(&persistence_notifier);
114 let exit_thread = Arc::new(AtomicBool::new(false));
115 let exit_thread_clone = exit_thread.clone();
116 thread::spawn(move || {
118 let &(ref persist_mtx, ref cnd) = &thread_notifier.persistence_lock;
119 let mut persistence_lock = persist_mtx.lock().unwrap();
120 *persistence_lock = true;
123 if exit_thread_clone.load(Ordering::SeqCst) {
129 // Check that we can block indefinitely until updates are available.
130 let _ = persistence_notifier.wait();
132 // Check that the PersistenceNotifier will return after the given duration if updates are
135 if persistence_notifier.wait_timeout(Duration::from_millis(100)) {
140 exit_thread.store(true, Ordering::SeqCst);
142 // Check that the PersistenceNotifier will return after the given duration even if no updates
145 if !persistence_notifier.wait_timeout(Duration::from_millis(100)) {