Merge pull request #502 from rloomba/rloomba/add_unregister_listener
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Wed, 19 Feb 2020 19:02:47 +0000 (19:02 +0000)
committerGitHub <noreply@github.com>
Wed, 19 Feb 2020 19:02:47 +0000 (19:02 +0000)
[chaininterface] Add ability for BlockNotifier to unregister listeners

1  2 
lightning/src/chain/chaininterface.rs

index 7a077e895a615fce737c4aa4db1cd13c0667e092,ad08c817dcea362187fb223629b989810b5309fd..0aa681cf78d19397d6fed74291d874e088cde15a
@@@ -19,6 -19,7 +19,7 @@@ use std::sync::atomic::{AtomicUsize, Or
  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 {
@@@ -126,7 -127,6 +127,7 @@@ pub trait FeeEstimator: Sync + Send 
  pub const MIN_RELAY_FEE_SAT_PER_1000_WEIGHT: u64 = 4000;
  
  /// Utility for tracking registered txn/outpoints and checking for matches
 +#[cfg_attr(test, derive(PartialEq))]
  pub struct ChainWatchedUtil {
        watch_all: bool,
  
@@@ -253,11 -253,22 +254,22 @@@ impl<'a, CL: Deref<Target = ChainListen
        }
  
        /// 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.
        ///
@@@ -306,17 -317,6 +318,17 @@@ pub struct ChainWatchInterfaceUtil 
        logger: Arc<Logger>,
  }
  
 +// We only expose PartialEq in test since its somewhat unclear exactly what it should do and we're
 +// only comparing a subset of fields (essentially just checking that the set of things we're
 +// watching is the same).
 +#[cfg(test)]
 +impl PartialEq for ChainWatchInterfaceUtil {
 +      fn eq(&self, o: &Self) -> bool {
 +              self.network == o.network &&
 +              *self.watched.lock().unwrap() == *o.watched.lock().unwrap()
 +      }
 +}
 +
  /// Register listener
  impl ChainWatchInterface for ChainWatchInterfaceUtil {
        fn install_watch_tx(&self, txid: &Sha256dHash, script_pub_key: &Script) {
@@@ -388,3 -388,76 +400,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)));
+       }
+ }