Enable limited use of asyncio with executors #971
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Resolves #962
Alternative to #963
This enables limited use of asyncio primitives with the rclpy executor. The most important parts are the changes to how
Future
instances handle__await__
and howTask
instances get scheduled. They allowTask
to tell when anasyncio
future isawait
ed, and they allowTask
to pause itself until the asyncio future completes. A consequence of this isTask
now requires an executor.There are limitations coming from asyncio not being thread safe.
Changes
Future.__await__
yields itself instead ofNone
- this allowsTask
to know if an rclpy Future is being awaitedExecutor.call_soon()
- This is likecreate_task()
, but if the callback is already a task then it won't create one. The naming is taking from asyncioExecutor._tasks
is cleared every time tasks are run.Task
instances schedule themselves when they become unblocked.Task
detects it's waiting on an asyncio future, then it will make the asyncio future schedule theTask
when it's doneasyncio.sleep(0)
in them already, but didn't create an asycnio event loop. Those uses have been removed.Task._lock
because it should never run in parallel now that it schedules itself.Task.executing()
because it would have needed theTask._lock
, and wasn't used anywhere else.