Repository: ros/ros_comm

Issue:
rospy: get_num_connections() does not return 0 when a topic is unsubscribed
Tested on latest indigo.
When I run a test node that displays the number of subscribers for my node using get_num_connections(), I see that this method returns 0 on startup, raise to 1 when I start a rostopic echo, and does not go back to 0 when the listener is killed.

The cpp equivalent using getNumSubscribers() works fine.

To reproduce:
Here is the node code.

```
import rospy
from std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    r = rospy.Rate(10) # 10hz
    while not rospy.is_shutdown():
        str = "hello world %s"%rospy.get_time()
        rospy.loginfo(str)
        print("subscribers: " , pub.get_num_connections())
        pub.publish(str)
        r.sleep()

if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException: pass
```

rostopic echo /chatter
=> The publisher console outputs "subscribers: 1"
Kill the rostopic echo
=> The publisher console outputs "subscribers: 1"

I was hoping to manage the node activity according to the number of subscribers to that node.



Discussion:
and by the way, @lsouchet tried in C++ with getNumSubscribers and that works there

Does it work as expected if queue_size=None (the old blocking-publisher code). If so, then this is likely a regression in the new non-blocking code -- not sure how that code eventually determines a connection is lost though.

I just tried with queue_size=None and it works indeed.

```
pub = rospy.Publisher('chatter', String, queue_size=None)
```

By roughly ebugging the code, it appears that the connection passed to the remove_connection() method is not the same as the one stored in the publisher connection list.
When going through https://github.com/ros/ros_comm/blob/indigo-devel/clients/rospy/src/rospy/topics.py#L342
Your are trying to disconnect:

```
<rospy.impl.tcpros_pubsub.QueuedConnection object at 0x7f9abeb29650>
```

While the connection list contains:

```
<rospy.impl.tcpros_base.TCPROSTransport object at 0x7f9abeb026d0>
```

I hope it can help.

The problem is that the connection is not removed on the publisher side because of the following check to fail when using the QueuedConnection: https://github.com/ros/ros_comm/blob/52e392111bf769b44b2bf696ef7a42a376cd1b84/clients/rospy/src/rospy/topics.py#L354

This is a regression of #308. Any fix will need to be backported to Hydro.


Resolving patch:
diff --git a/clients/rospy/src/rospy/topics.py b/clients/rospy/src/rospy/topics.py
--- a/clients/rospy/src/rospy/topics.py
+++ b/clients/rospy/src/rospy/topics.py
@@ -351,8 +351,17 @@ def _remove_connection(self, connections, c):
             c.close()
         except:
             pass
+        # while c might be a rospy.impl.tcpros_base.TCPROSTransport instance
+        # connections might only contain the rospy.impl.tcpros_pubsub.QueuedConnection proxy
+        # finding the "right" connection is more difficult then
         if c in connections:
             connections.remove(c)
+        # therefore additionally check for fileno equality if available
+        elif c.fileno():
+            matching_connections = [
+                conn for conn in connections if conn.fileno() == c.fileno()]
+            if len(matching_connections) == 1:
+                connections.remove(matching_connections[0])
 
     def add_connection(self, c):
         """


Test patch: