-
Notifications
You must be signed in to change notification settings - Fork 225
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
Asynchronous client calls in a timer cause the timer to never execute a second time. #1018
Comments
The def spin_until_future_complete(
node: 'Node',
future: Future,
executor: 'Executor' = None,
timeout_sec: float = None
) -> None:
"""
Execute work until the future is complete.
Callbacks and other work will be executed by the provided executor until ``future.done()``
returns ``True`` or the context associated with the executor is shutdown.
:param node: A node to add to the executor to check for work.
:param future: The future object to wait on.
:param executor: The executor to use, or the global executor if ``None``.
:param timeout_sec: Seconds to wait. Block until the future is complete
if ``None`` or negative. Don't wait if 0.
"""
executor = get_global_executor() if executor is None else executor
try:
executor.add_node(node)
executor.spin_until_future_complete(future, timeout_sec)
finally:
executor.remove_node(node)
So IMO, rclpy.spin_xxx may conflict with executor.spin_xxx (which you defined), it's better to use executor.spin_until_future_complete directly. |
This may be a symptom of the cause found in #1016 |
Here's my solution to the client: ...
def send_request(self, a, b):
self.get_logger().info("send_request: enter")
self.req.a = a
self.req.b = b
self.future = self.cli.call_async(self.req)
while rclpy.ok():
if self.future.done() and self.future.result():
self.get_logger().info("send_request: exit")
return self.future.result()
return None
... |
I think this is adding the same node base to the different and multiple executor, which does not work. (this is because GuardCondition does not work, @llapx 's suggestion works, but i would recommend that you could use ...<snip>
async def send_request(self, a, b):
self.get_logger().info("send_request: enter")
self.req.a = a
self.req.b = b
return await self.cli.call_async(self.req)
async def main_loop(self) -> None:
response = await self.send_request(4, 2)
self.get_logger().info(
"Result of add_two_ints: for %d + %d = %d" % (4, 2, response.sum)
)
...<snip> |
… a second time ros2/rclpy#1018 Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
#1018 (comment) and #1018 (comment) seem not good to me. I suggest using + self.executor.spin_until_future_complete(self.future)
- rclpy.spin_until_future_complete(self, self.future) in the example. Let me share my understanding of this issue.
Please notice that there is a property named executor in the Node. rclpy/rclpy/rclpy/executors.py Line 241 in e18084f
after calling Line 247 in bce7e89
the Lines 279 to 283 in e18084f
(This is the reason why the executor = MultiThreadedExecutor() not spin the events of minimal_client any more.)
|
All, thanks for all the discussion and potential solutions. I would just like to note that the two solutions that @fujitatomoya and @iuhilnehc-ynos brought up (using |
@molasses11 thank you so much for being constructive ! those are just suggestions, i am not saying that this is not the problem, that will be some areas that we can refine.
let's keep this open, i am not sure i can have bandwidth for this soon though. |
I can confirm I am experiencing the same issue. I tried experimenting with Executors and MutuallyExclusiveCallbackGroup like molasses11 with code which has timers/loops in it, and was able to get proper (non-blocking) functionality using Services by creating another node (within the same node using This work-around however does not work with Actions. With the action all ROS functionality of the node gets frozen until the timer/loop finishes (ex: unable to get parameters or make service requests from the same node). And depending on how you're doing the looping (while loop with rate object, rclpy.spin_once, executor.spin_once) I sometimes have it where the Node refuses and new requests once the loop is finished (even if it accepted requests fine while looping). I've found using MutuallyExclusiveCallbackGroup for the service callback option, and creating a custom executor for the node and using the Currently reverting my Action to a Service since I can get around not needing feedback and request cancellation features for right now in my application, but hope this issue gets resolved. Wanted to mention all this in case it helps show a correlation of the issue with client calls blocking future requests of the Node. |
We experienced similar issuses in different contexts. I think the problem is deeper, not only related to client calls and timers. Essentially all implementations of
I do think that recursive spinning is a desirable feature and apparently many users rely on that for implementing timeouts without blocking all other callbacks. Before recent #1188 calling a service with a timeout that works in any context was surprisingly hard, but the synchronous Suggestion: So probably the "best" solution would be to try adding the node to the executor for spinning, but keep the return value and only remove it in try:
was_added = executor.add_node(node)
executor.spin_until_future_complete(future, timeout_sec)
finally:
if was_added:
executor.remove_node(node) of with a context manager that temporarily adds the node to the executor if it was not added before, instead of the repeated Alternatively there must be a way to call Something like node.executor.spin_until_future_complete(future, timeout_sec) instead of rclpy.spin_until_future_complete(future, timeout_sec) should also solve the problem described in this issue, and similar ones. When checking on how to find the current Afaik there is currently no way to get the executor of the current thread, if any, without a reference to the node? I think Interestingly, in rclcpp recursively spinning a node results in an exception being thrown in |
Bug report
Required Info:
Steps to reproduce issue
Use the minimal service server from the examples
and a slightly modified service client:
Run the service server in one terminal and the client in another.
Expected behavior
You repeatedly see the client call the server every second and get a response.
Actual behavior
The client runs correctly once, but then hangs (never executes the timer loop again)
Additional information
Previous to the 2022-09-28 patch release of foxy, a workaround was to use a synchronous call. However, that is non-functional now (see #1016 for more info/workarounds on that issue). As this behavior is functional in rclcpp in foxy, and seems to be what the documentation of foxy recommends it would be good to have this be functional.
The text was updated successfully, but these errors were encountered: