Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node has a reference to its executor #218

Merged
merged 1 commit into from
Aug 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion rclpy/rclpy/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import multiprocessing
from threading import Condition
from threading import Lock
from threading import RLock

from rclpy.impl.implementation_singleton import rclpy_implementation as _rclpy
from rclpy.task import Task
Expand Down Expand Up @@ -111,7 +112,7 @@ class Executor:
def __init__(self):
super().__init__()
self._nodes = set()
self._nodes_lock = Lock()
self._nodes_lock = RLock()
# Tasks to be executed (oldest first) 3-tuple Task, Entity, Node
self._tasks = []
self._tasks_lock = Lock()
Expand Down Expand Up @@ -182,6 +183,7 @@ def add_node(self, node):
with self._nodes_lock:
if node not in self._nodes:
self._nodes.add(node)
node.executor = self
# Rebuild the wait set so it includes this new node
_rclpy.rclpy_trigger_guard_condition(self._guard_condition)
return True
Expand Down
21 changes: 21 additions & 0 deletions rclpy/rclpy/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import weakref

from rclpy.callback_groups import MutuallyExclusiveCallbackGroup
from rclpy.client import Client
from rclpy.clock import ROSClock
Expand Down Expand Up @@ -88,6 +90,25 @@ def __init__(self, node_name, *, cli_args=None, namespace=None, use_global_argum
self._time_source = TimeSource(node=self)
self._time_source.attach_clock(self._clock)

self.__executor_weakref = None

@property
def executor(self):
"""Get the executor if the node has been added to one, else return None."""
if self.__executor_weakref:
return self.__executor_weakref()

@executor.setter
def executor(self, new_executor):
"""Set or change the executor the node belongs to."""
current_executor = self.executor
if current_executor is not None:
current_executor.remove_node(self)
if new_executor is None:
self.__executor_weakref = None
elif new_executor.add_node(self):
self.__executor_weakref = weakref.ref(new_executor)

@property
def handle(self):
return self._handle
Expand Down
47 changes: 47 additions & 0 deletions rclpy/test/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

import unittest
from unittest.mock import Mock

from rcl_interfaces.srv import GetParameters
import rclpy
Expand Down Expand Up @@ -128,6 +129,52 @@ def test_node_logger(self):
node_logger.set_level(rclpy.logging.LoggingSeverity.INFO)
node_logger.debug('test')

def test_initially_no_executor(self):
node = rclpy.create_node('my_node')
try:
assert node.executor is None
finally:
node.destroy_node()

def test_set_executor_adds_node_to_it(self):
node = rclpy.create_node('my_node')
executor = Mock()
executor.add_node.return_value = True
try:
node.executor = executor
assert id(executor) == id(node.executor)
finally:
node.destroy_node()
executor.add_node.assert_called_once_with(node)

def test_set_executor_removes_node_from_old_executor(self):
node = rclpy.create_node('my_node')
old_executor = Mock()
old_executor.add_node.return_value = True
new_executor = Mock()
new_executor.add_node.return_value = True
try:
node.executor = old_executor
assert id(old_executor) == id(node.executor)
node.executor = new_executor
assert id(new_executor) == id(node.executor)
finally:
node.destroy_node()
old_executor.remove_node.assert_called_once_with(node)
new_executor.remove_node.assert_not_called()

def test_set_executor_clear_executor(self):
node = rclpy.create_node('my_node')
executor = Mock()
executor.add_node.return_value = True
try:
node.executor = executor
assert id(executor) == id(node.executor)
node.executor = None
assert node.executor is None
finally:
node.destroy_node()


class TestCreateNode(unittest.TestCase):

Expand Down