add functionality for BlockNotifier to unregister a previously registered listener...
authorRyan Loomba <ryan@loomba.io>
Sat, 15 Feb 2020 08:04:09 +0000 (00:04 -0800)
committerRyan Loomba <ryan@loomba.io>
Tue, 18 Feb 2020 05:33:56 +0000 (21:33 -0800)
lightning/src/chain/chaininterface.rs
lightning/src/ln/mod.rs

index 0845eb5fc6965392cf6340a5a682bffe70ebe7db..ad08c817dcea362187fb223629b989810b5309fd 100644 (file)
@@ -19,6 +19,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
 use std::collections::HashSet;
 use std::ops::Deref;
 use std::marker::PhantomData;
+use std::ptr;
 
 /// Used to give chain error details upstream
 pub enum ChainError {
@@ -252,11 +253,22 @@ impl<'a, CL: Deref<Target = ChainListener + 'a> + 'a> BlockNotifier<'a, CL> {
        }
 
        /// Register the given listener to receive events.
-       // TODO: unregister
        pub fn register_listener(&self, listener: CL) {
                let mut vec = self.listeners.lock().unwrap();
                vec.push(listener);
        }
+       /// Unregister the given listener to no longer
+       /// receive events.
+       ///
+       /// If the same listener is registered multiple times, unregistering
+       /// will remove ALL occurrences of that listener. Comparison is done using
+       /// the pointer returned by the Deref trait implementation.
+       pub fn unregister_listener(&self, listener: CL) {
+               let mut vec = self.listeners.lock().unwrap();
+               // item is a ref to an abstract thing that dereferences to a ChainListener,
+               // so dereference it twice to get the ChainListener itself
+               vec.retain(|item | !ptr::eq(&(**item), &(*listener)));
+       }
 
        /// Notify listeners that a block was connected given a full, unfiltered block.
        ///
@@ -376,3 +388,76 @@ impl ChainWatchInterfaceUtil {
                watched.does_match_tx(tx)
        }
 }
+
+#[cfg(test)]
+mod tests {
+       use ln::functional_test_utils::{create_node_cfgs};
+       use super::{BlockNotifier, ChainListener};
+       use std::ptr;
+
+       #[test]
+       fn register_listener_test() {
+               let node_cfgs = create_node_cfgs(1);
+               let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor.clone());
+               assert_eq!(block_notifier.listeners.lock().unwrap().len(), 0);
+               let listener = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener;
+               block_notifier.register_listener(listener);
+               let vec = block_notifier.listeners.lock().unwrap();
+               assert_eq!(vec.len(), 1);
+               let item = vec.first().clone().unwrap();
+               assert!(ptr::eq(&(**item), &(*listener)));
+       }
+
+       #[test]
+       fn unregister_single_listener_test() {
+               let node_cfgs = create_node_cfgs(2);
+               let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor.clone());
+               let listener1 = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener;
+               let listener2 = &node_cfgs[1].chan_monitor.simple_monitor as &ChainListener;
+               block_notifier.register_listener(listener1);
+               block_notifier.register_listener(listener2);
+               let vec = block_notifier.listeners.lock().unwrap();
+               assert_eq!(vec.len(), 2);
+               drop(vec);
+               block_notifier.unregister_listener(listener1);
+               let vec = block_notifier.listeners.lock().unwrap();
+               assert_eq!(vec.len(), 1);
+               let item = vec.first().clone().unwrap();
+               assert!(ptr::eq(&(**item), &(*listener2)));
+       }
+
+       #[test]
+       fn unregister_single_listener_ref_test() {
+               let node_cfgs = create_node_cfgs(2);
+               let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor.clone());
+               block_notifier.register_listener(&node_cfgs[0].chan_monitor.simple_monitor as &ChainListener);
+               block_notifier.register_listener(&node_cfgs[1].chan_monitor.simple_monitor as &ChainListener);
+               let vec = block_notifier.listeners.lock().unwrap();
+               assert_eq!(vec.len(), 2);
+               drop(vec);
+               block_notifier.unregister_listener(&node_cfgs[0].chan_monitor.simple_monitor);
+               let vec = block_notifier.listeners.lock().unwrap();
+               assert_eq!(vec.len(), 1);
+               let item = vec.first().clone().unwrap();
+               assert!(ptr::eq(&(**item), &(*&node_cfgs[1].chan_monitor.simple_monitor)));
+       }
+
+       #[test]
+       fn unregister_multiple_of_the_same_listeners_test() {
+               let node_cfgs = create_node_cfgs(2);
+               let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor.clone());
+               let listener1 = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener;
+               let listener2 = &node_cfgs[1].chan_monitor.simple_monitor as &ChainListener;
+               block_notifier.register_listener(listener1);
+               block_notifier.register_listener(listener1);
+               block_notifier.register_listener(listener2);
+               let vec = block_notifier.listeners.lock().unwrap();
+               assert_eq!(vec.len(), 3);
+               drop(vec);
+               block_notifier.unregister_listener(listener1);
+               let vec = block_notifier.listeners.lock().unwrap();
+               assert_eq!(vec.len(), 1);
+               let item = vec.first().clone().unwrap();
+               assert!(ptr::eq(&(**item), &(*listener2)));
+       }
+}
index 9a2a90fd4fd6e9444bf3a5cbcaa8f68b97be9407..14864ddb53b1496b430b70e8adef530ec145ef91 100644 (file)
@@ -27,7 +27,8 @@ mod onion_utils;
 mod wire;
 
 #[cfg(test)]
-#[macro_use] mod functional_test_utils;
+#[macro_use]
+pub(crate) mod functional_test_utils;
 #[cfg(test)]
 mod functional_tests;
 #[cfg(test)]