-
Notifications
You must be signed in to change notification settings - Fork 259
Feature: new asyncio executor #1399
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
base: rolling
Are you sure you want to change the base?
Conversation
|
I would love to see this make its way into main. My main frustration with rclpy is that it seems to ignore the "Pythonic" way of doing things in favor of its own way. Asyncio integration would make ROS2 much more pleasant to work with in Python. |
|
Mentioning #1461 |
708ff91 to
53db751
Compare
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: = <[email protected]>
Signed-off-by: Nadav Elkabets <[email protected]>
Signed-off-by: Nadav Elkabets <[email protected]>
Signed-off-by: Nadav Elkabets <[email protected]>
Signed-off-by: Nadav Elkabets <[email protected]>
Signed-off-by: Nadav Elkabets <[email protected]>
Signed-off-by: Nadav Elkabets <[email protected]>
Signed-off-by: Nadav Elkabets <[email protected]>
f4afea4 to
429a60a
Compare
UpdateI worked through a couple of design iterations, and I believe I settled on one that almost exactly matches the behavior of SingleThreadedExecutor. @sloretz you came up in the last working group meeting as the most qualified maintainer to review this PR. |
|
Thank you for the PR!
I'm giving it a skim now, but it might take me a while to review it fully. I think we could take a couple changes right away. Would you be willing to create two PRs and ping me as a reviewer?
One of the reasons |
|
Guard condition and waitable support will be important (Actions are implemented using waitables). Are there any technical blockers to implementing them in the asyncio executor? Callback groups support isn't necessary. Callback groups were created for C++, but the problem they solve in C++ is solved in Python by using coroutines. I don't think we need callback groups in rclpy at all. |
Both coroutines and multi-threading are different methods to achieve concurrency. Without asyncio, the only way to utilize many libraries (serial, HTTP, DB drivers) was multi-threading, but now this is no longer the case.
Asyncio is not thread safe so the AsyncioExecutor isn't either.
Regarding guard conditions, the classic “wake the wait-set from a different thread”
As discussed in one of the working group meetings,
Sounds good. I'll let you know when they're ready. |
|
Motivation
While rclpy is task-based and built with asynchronous support, its custom implementation imposes significant limitations and lacks integration with Python's asyncio ecosystem. Integrating rclpy nodes with modern asyncio-based Python libraries like FastAPI and pyserial-asyncio is difficult and often forces developers into complex multi-threaded solutions.
Inspired by @sloretz's PR #971, this PR introduces an asyncio-based executor that runs nodes entirely on the asyncio event loop, which has become the de facto standard for IO programming in the Python community.
Design considerations
C++ vs. Python Implementation
py::exec. In addition, casting the EventsExecutor type to Executor feels to me like unhealthy practice.Callback Handling
asyncio.call_soon_threadsafe. Since most of asyncio’s core is in C, this amounts to grabbing a lock, enqueueing the task, and writing to a wake-fd, which is an extremely lightweight operation.Futures Compatibility
get_loop()and_asyncio_future_blockingapi.loop.create_future(). Asyncio even enforces in runtime that the future belongs to the running loop. In contrast,rclpylets you callclient.call_async()without an executor, which is only set when the response arrives.Spin Behavior
spin_once()executes only one callback per invocation.asyncio’sloop.run_forever()repeatedly calls_run_once()until stopped, executing all ready callbacks each cycle.asyncio.Queueis utilized to queue entity callbacks and user created tasks for spin_once. Users might choose to create a task using theexecutor.create_taskapi or theexecutor.loop.create_taskapi. The first will match the behavior of spin_once, while the second will be more efficient.Changes
AsyncioExecutorclass that runs entity events as asyncio tasks on the event loop.set_on_new_<message,request,response>_callbackAPI in Python Subscription, Service, and Client.set_on_reset_callbackAPI in Python Timer.ExecutorBaseclass to share code like_take_subscriptionwith WaitSet executors.executor.create_future(), encouraging users to create futures bound to the executor.AsyncioClockenablingsleep_until_asyncandsleep_for_asyncrclpy.Futuretoyield selfexecutor._resume_taskmethodSupported & Unsupported Entities
Supported
Not Supported
set_on_ready_callbackapproach of rclcppUpdates
test_rclpy_performance.pyscript from the EventsExecutor PR on the asyncio executor yielded fantastic results!