فهرست منبع

net/direct_session: add `channels_usage` and `cleanup_channel()`

epiphany 9 ماه پیش
والد
کامیت
b9dcad4072
1فایلهای تغییر یافته به همراه75 افزوده شده و 9 حذف شده
  1. 75 9
      src/net/session/direct_session.rs

+ 75 - 9
src/net/session/direct_session.rs

@@ -73,6 +73,8 @@ pub struct DirectSession {
     retries_tasks: Arc<AsyncMutex<HashMap<Url, Arc<StoppableTask>>>>,
     retries_tasks: Arc<AsyncMutex<HashMap<Url, Arc<StoppableTask>>>>,
     /// Peer discovery task
     /// Peer discovery task
     peer_discovery: Arc<PeerDiscovery>,
     peer_discovery: Arc<PeerDiscovery>,
+    /// Channel ID -> usage count
+    channels_usage: Arc<AsyncMutex<HashMap<u32, u32>>>,
 }
 }
 
 
 impl DirectSession {
 impl DirectSession {
@@ -83,6 +85,7 @@ impl DirectSession {
             channel_builder: Arc::new(AsyncMutex::new(ChannelBuilder::new(session.clone()))),
             channel_builder: Arc::new(AsyncMutex::new(ChannelBuilder::new(session.clone()))),
             retries_tasks: Arc::new(AsyncMutex::new(HashMap::new())),
             retries_tasks: Arc::new(AsyncMutex::new(HashMap::new())),
             peer_discovery: PeerDiscovery::new(session.clone()),
             peer_discovery: PeerDiscovery::new(session.clone()),
+            channels_usage: Arc::new(AsyncMutex::new(HashMap::new())),
         })
         })
     }
     }
 
 
@@ -109,23 +112,32 @@ impl DirectSession {
         self.peer_discovery.notify();
         self.peer_discovery.notify();
     }
     }
 
 
-    /// Create a new channel to `addr` in the direct session.
-    pub async fn create_channel(&self, addr: &Url) -> Result<ChannelPtr> {
+    /// If there is an existing channel to the same address, this method will
+    /// return it (even if the channel was not created by the direct session).
+    /// Otherwise it will create a new channel to `addr` in the direct session.
+    pub async fn get_channel(&self, addr: &Url) -> Result<ChannelPtr> {
+        let channels = self.p2p().hosts().channels();
+        let channel = channels.iter().find(|&chan| chan.address() == addr).cloned();
+        if let Some(channel) = channel {
+            let mut channels_usage = self.channels_usage.lock().await;
+            let usage_count = channels_usage.get_mut(&channel.info.id);
+            if let Some(count) = usage_count {
+                *count += 1;
+            }
+            return Ok(channel)
+        }
         self.channel_builder.lock().await.new_channel(addr).await
         self.channel_builder.lock().await.new_channel(addr).await
     }
     }
 
 
     /// Try to create a new channel until it succeeds, then notify `channel_pub`.
     /// Try to create a new channel until it succeeds, then notify `channel_pub`.
     /// If it fails to create a channel, a task will sleep
     /// If it fails to create a channel, a task will sleep
     /// `outbound_connect_timeout` seconds and try again.
     /// `outbound_connect_timeout` seconds and try again.
-    pub async fn create_channel_with_retries(
-        &self,
-        addr: Url,
-        channel_pub: PublisherPtr<ChannelPtr>,
-    ) {
+    pub async fn get_channel_with_retries(&self, addr: Url, channel_pub: PublisherPtr<ChannelPtr>) {
         let channel_builder = self.channel_builder.clone();
         let channel_builder = self.channel_builder.clone();
         let task = StoppableTask::new();
         let task = StoppableTask::new();
         let retries_tasks_lock = self.retries_tasks.clone();
         let retries_tasks_lock = self.retries_tasks.clone();
         let mut retries_tasks = self.retries_tasks.lock().await;
         let mut retries_tasks = self.retries_tasks.lock().await;
+        let channels_usage = self.channels_usage.clone();
         let p2p = self.p2p().clone();
         let p2p = self.p2p().clone();
         retries_tasks.insert(addr.clone(), task.clone());
         retries_tasks.insert(addr.clone(), task.clone());
         drop(retries_tasks);
         drop(retries_tasks);
@@ -133,6 +145,22 @@ impl DirectSession {
         task.clone().start(
         task.clone().start(
             async move {
             async move {
                 loop {
                 loop {
+                    // Check if there is already a channel to this addr
+                    let channels = p2p.hosts().channels();
+                    let channel = channels.iter().find(|&chan| chan.address() == &addr).cloned();
+                    if let Some(channel) = channel {
+                        let mut chan_usage = channels_usage.lock().await;
+                        let usage_count = chan_usage.get_mut(&channel.info.id);
+                        if let Some(count) = usage_count {
+                            *count += 1;
+                        }
+                        channel_pub.notify(channel).await;
+                        let mut retries_tasks = retries_tasks_lock.lock().await;
+                        retries_tasks.remove(&addr);
+                        break
+                    }
+
+                    // Try to create a new channel
                     let mut builder = channel_builder.lock().await;
                     let mut builder = channel_builder.lock().await;
                     let res = builder.new_channel(&addr).await;
                     let res = builder.new_channel(&addr).await;
                     match res {
                     match res {
@@ -165,6 +193,37 @@ impl DirectSession {
             self.p2p().executor(),
             self.p2p().executor(),
         );
         );
     }
     }
+
+    /// Close a direct channel if it's not used by anything.
+    /// `AsyncDrop` would be great here (<https://doc.rust-lang.org/std/future/trait.AsyncDrop.html>)
+    /// but it's still in nightly. For now you must call this method manually
+    /// once you are done with a direct channel.
+    /// Returns `true` if the channel is stopped.
+    pub async fn cleanup_channel(&self, channel: ChannelPtr) -> bool {
+        if channel.session_type_id() & SESSION_DIRECT == 0 {
+            // Do nothing if this is not a channel created by the direct session
+            return false
+        }
+
+        let mut channels_usage = self.channels_usage.lock().await;
+        let usage_count = channels_usage.get_mut(&channel.info.id);
+        if usage_count.is_none() {
+            channel.stop().await;
+            return true
+        }
+        let usage_count = usage_count.unwrap();
+        if *usage_count > 0 {
+            *usage_count -= 1;
+        }
+
+        if *usage_count == 0 {
+            channels_usage.remove(&channel.info.id);
+            channel.stop().await;
+            return true
+        }
+
+        false
+    }
 }
 }
 
 
 #[async_trait]
 #[async_trait]
@@ -193,6 +252,10 @@ impl ChannelBuilder {
         self.session.upgrade().unwrap()
         self.session.upgrade().unwrap()
     }
     }
 
 
+    fn p2p(&self) -> P2pPtr {
+        self.session().p2p()
+    }
+
     fn connector(&mut self) -> Arc<Connector> {
     fn connector(&mut self) -> Arc<Connector> {
         match &self.connector {
         match &self.connector {
             Some(c) => c.clone(),
             Some(c) => c.clone(),
@@ -272,7 +335,10 @@ impl ChannelBuilder {
                     .register_channel(channel.clone(), self.session().p2p().executor())
                     .register_channel(channel.clone(), self.session().p2p().executor())
                     .await
                     .await
                 {
                 {
-                    Ok(()) => Ok(channel),
+                    Ok(()) => {
+                        self.session().channels_usage.lock().await.insert(channel.info.id, 1);
+                        Ok(channel)
+                    }
                     Err(e) => {
                     Err(e) => {
                         warn!(
                         warn!(
                             target: "net::direct_session",
                             target: "net::direct_session",
@@ -415,7 +481,7 @@ impl PeerDiscovery {
                         .container
                         .container
                         .fetch_random_with_schemes(color.clone(), &allowed_transports)
                         .fetch_random_with_schemes(color.clone(), &allowed_transports)
                     {
                     {
-                        channel = self.p2p().session_direct().create_channel(&entry.0).await.ok();
+                        channel = self.p2p().session_direct().get_channel(&entry.0).await.ok();
                         break;
                         break;
                     }
                     }
                 }
                 }