-
Notifications
You must be signed in to change notification settings - Fork 233
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
Futures and coroutines #166
Conversation
Adds Task and Future classes based on asyncio Callbacks can be coroutines, the executor runs them to completion
a0d88c8
to
d29fa97
Compare
If a task is executing, acquired its lock, but not set _executing = True yet and another thread calls the same task it is possible for the second thread to be blocked while the first thread executes. The intention is for the second thread to return immediately.
Oops, looks like the description I put on this task was completely wrong and from another PR. It's fixed now. |
@ros2/team Friendly ping. I would appreciate a review of this one. It is a prerequisite to multiple concurrent client requests (#170) and a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The patch looks reasonable to me. I haven't tried to use the new API though.
rclpy/rclpy/executors.py
Outdated
""" | ||
Add a callback or coroutine to be executed during :meth:`spin` and return a Future. | ||
|
||
Arguments to this function are passed to the callback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this also permit keyword arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, Added in 5f2cb1e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't think about it before. It might be necessary to pass executor
using a setter function instead to avoid a collision if the callback wants to use a keyword argument with that name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 5f2cb1e the expansion of args
and kwargs
in Task.__init__()
was removed to prevent that collision. The expansion on executor.create_task()
cannot conflict because they're now passed to Task.__init__()
as a sequence and dictionary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect!
Hi @sloretz! Is it possible/easy to make the equivalent of (We have been trying to find a robust pattern for waiting in the execute functions of action servers in rclpy. The only pattern that works for us 100% reliably so far is using a SingleThreadedExecutor and |
This adds coroutine support to the executor and is part of service feature parity #123 and
wait_for_service
without aGraphListener
#58.Any callback can now be a coroutine function (
async def
). If the callback is a coroutine and it becomes blocked the executor will move on and resume executing it later. The details of this are handled in the classTask
which is a class very similar to asyncio.Task.asyncio
can't be used directly because it is not thread safe.This PR also adds a
Future
class that represents the result of a stored task. It is not used in this PR, but an example of using it is in #170.CI ( with 5f2cb1e)