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

client.call blocks the thread #181

Closed
minggangw opened this issue Apr 4, 2018 · 3 comments
Closed

client.call blocks the thread #181

minggangw opened this issue Apr 4, 2018 · 3 comments
Labels
question Further information is requested

Comments

@minggangw
Copy link

Bug report

Required Info:

  • Operating System:
    • Ubuntu 16.04
  • Installation type:
    • from source
  • Version or commit hash:
    • f776f597d7d5b325ab5d49486115b5267565937e2
  • DDS implementation:
  • Client library (if applicable):

Steps to reproduce issue

from example_interfaces.srv import AddTwoInts

import rclpy


def main(args=None):
    rclpy.init(args=args)
    node = rclpy.create_node('minimal_client')
    cli = node.create_client(AddTwoInts, 'add_two_ints')

    req = AddTwoInts.Request()
    req.a = 41
    req.b = 1
    while not cli.wait_for_service(timeout_sec=1.0):
        node.get_logger().info('service not available, waiting again...')

    cli.call(req)

    node.get_logger().info(
        'Result of add_two_ints: for %d + %d = %d' %
        (req.a, req.b, cli.response.sum))

    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

Expected behavior

The client will receive the response from service side.

Actual behavior

The thread gets blocked after the line cli.call(req)

Additional information

suspicious commit 0c2ee4c

@minggangw
Copy link
Author

After the call_async was introduced, I am not sure the code snippet above is right. Please correct me if I am wrong.

@sloretz
Copy link
Contributor

sloretz commented Apr 4, 2018

Hi @minggangw thanks for the bug report. I'll close this since it looks to be working as intended. It blocks forever because there isn't an executor running. An executor must be running to unblock client.call when a service response is received.

Another detail is cli.call(req) returns the response. The client no longer has a response attribute.

The smallest change to get the code working is to replace cli.call(req) with

    future = cli.call_async(req)
    rclpy.spin_until_future_complete(node, future)
    response = future.result()

That invokes a global executor which is single-threaded. If the global executor isn't good enough for you (say you'd like callbacks to run in multiple threads), then an executor will need to be started separately.

from example_interfaces.srv import AddTwoInts
from rclpy.executors import MultiThreadedExecutor

import rclpy
import threading


def run_executor(executor, node):
    executor.add_node(node)
    try:
        executor.spin()
    finally:
        executor.shutdown()


def main(args=None):
    rclpy.init(args=args)
    node = rclpy.create_node('minimal_client')
    cli = node.create_client(AddTwoInts, 'add_two_ints')

    # Run executor that uses a thread pool in another thread so main thread is free to do what it wants
    exec = MultiThreadedExecutor(num_threads=4)
    thread = threading.Thread(target=run_executor, args=(exec, node), daemon=True)
    thread.start()

    req = AddTwoInts.Request()
    req.a = 41
    req.b = 1
    while not cli.wait_for_service(timeout_sec=1.0):
        node.get_logger().info('service not available, waiting again...')

    response = cli.call(req)

    node.get_logger().info(
        'Result of add_two_ints: for %d + %d = %d' %
        (req.a, req.b, response.sum))

    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

Save this as client_call_example.py

  1. ros2 run examples_rclpy_minimal_service service
  2. python3 client_call_example.py

See also ros2/examples an example of a custom executor and compose multiple nodes in a single process.

@sloretz sloretz closed this as completed Apr 4, 2018
@sloretz sloretz added the question Further information is requested label Apr 4, 2018
@minggangw
Copy link
Author

Thanks for your explanation @sloretz .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants