feat: add coroutine support for client-side asynchronous calls#527
feat: add coroutine support for client-side asynchronous calls#527alexcani wants to merge 9 commits intoKistler-Group:masterfrom
Conversation
Include .vscode/ in .gitignore to prevent IDE-specific files from being tracked in the repository.
Prepare for C++20 coroutine support for asynchronous D-Bus method calls by introducing the Awaitable<T> class, enabling co_await syntax. This class contains the three necessary methods to support the co_await operator: await_ready, await_suspend and await_resume. A shared data structure is used to hold data needed by the callback that will resume the coroutine and feed in the result or exception. This work similarly to how a future/promise pair work. Potential race conditions in the multithreaded scenario are covered by atomic operations on an AwaitableState value, preventing situations that could lead to a double coroutine resume or permanent suspension.
Add three new flavors of the callMethodAsync method in the IProxy interface that use the with_awaitable_t tag to return an Awaitable<MethodReply>. Implementation relies on the callback-based mechanism to insert the results into the shared AwaitableData, transition the state to Completed and resume the coroutine.
Add a .getResultAsAwaitable method to each of the relevant high-level API helpers (AsyncMethodInvoker, AsyncPropertyGetter, AsyncPropertySetter, and AsyncAllPropertiesGetter). Implementation is similar to the low-level API: rely on the .uponReplyInvoke() mechanism to set the results on the shared data, atomically flip the state, and resume the suspended coroutine.
Adapt the StandardInterface.h classes to include an awaitable-based overload of the async methods.
…ation The current implementation relies on two flags to determine if async is enabled for methods/properties. In preparation to introduce the generation of the coroutine-friendly awaitable methods as an async implementation, refactor this as an enum containing the implementations and an std::optional to mark simultaneously if async is enabled and the method. There are no behavioral changes or new functonality. C++ 17 is needed for std::optional, therefore the used C++ standard for the tool has been changed.
Use the new awaitable-returning methods from the high level API to add support to a third async implementation mode alongside the existing callback and future implementations. Add a new enum entry to AsyncImpl and parse the "awaitable" or "coroutine" values in the relevant annotations for methods and properties. Methods annotated with this implementation will return sdbus::Awaitable<ReturnType> and call .getResultAsAwaitable<>() on the high-level API. Properties follow the same pattern, returning sdbus::Awaitable<sdbus::Variant> for getters and sdbus::Awaitable<void> for setters. The nested ternary expressions for determining return types have been refactored into explicit if-else blocks for improved readability and to accommodate the additional implementation type. The existing callback and future implementations remain unchanged in behavior.
|
Hi there!
Also thanks for the work you put in the project, I always had a good time using it. Please let me know if there are further changes or design considerations needed |
Introduce new awaitable methods in TestProxy for asynchronous operations and add corresponding integration tests in DBusAwaitableMethodsTests.cpp to validate functionality. The test uses a simple coroutine task type which uses a promise/future pair to pass data between the callback and the coroutine, leveraging the fact that the tests are all multithreaded. This approach would not work in a single-threaded scenario, as the future's .get() method would block the thread.
Update the main tutorial with the new awaitable-based async method.
e8ceebb to
3988cd7
Compare
|
Hey @alexcani. Thanks a lot for contributing and submitting the PR! I started looking into that but am a bit overloaded currently, I'll definitely come back to this asap. |
|
Hi! I reviewed the PR. Appears to me to be one of the best, well-done PRs I've reviewed on this project. It is enjoyable to see changes done to nice little details and across all levels. I have no experience with coroutines, so I had to do the research first. Still, given lack of experience, I don't feel 100% confident about all the details of good coroutines design. But given the quality of your work, I think I can trust you. However, I did some deeper reflection about things like overall design, thread coordination, exception handling, etc. -- and when I tried to think about them out-of-the-box, I got back to your solution, understanding your motives. Again, I see you've put an aspect of quality and craftsmanship to your work, and this is something I highly appreciate. One thing I was asking myself was -- in
I've already prepared a local patch for that. It works nicely. I can push that to your branch if you're okay with that, for you to review. Let me know. In the code I touched, I also changed the formatting a bit to stay consistent with the surrounding code. Regarding code style, yes, clang-format would be great. I have that on my TODO list. Regarding copyright, all fine with me. Regarding API, yes, it is currently C++17 compatible. Would be great to use feature macros to have this feature conditionally available. Regarding Awaitable public c-tor, I don't have a strong opinion, but for consistency with other similar solutions in the library, I would slightly tend towards private c-tor and befriending the relevant classes. It's already done this way in other places in the library. Additionally, I would add Tests LGTM. Clang-tidy job is failing -- shall I address the reported issues, or will you? I'm glad you've enjoyed using the library so far. That was my ultimate design goal, and I've put my best into it. |
|
Hi!
Thanks for taking the time to review and also for the kind words!
Of course, feel free to push the patches you have prepared. std::variant
makes sense as well, i think I didnt use it just based on personal taste.
I'm currently in vacation with no access to my setup (replying to this via
email on my phone), so it will take a couple weeks before I'm able to put
some work into it, however i can work on the other points (c++17 compat,
the privatr ctor + friends, the assertions and the linter issues) when I'm
back if you prefer. Just let me know!
Also i was thinking if we need some tests for making sure the feature is
properly toggled via the feature macros on different compilers.
Kind regards,
Alex
Em qua., 25 de mar. de 2026, 8:51 PM, Stanislav Angelovič <
***@***.***> escreveu:
… *sangelovic* left a comment (Kistler-Group/sdbus-cpp#527)
<#527?email_source=notifications&email_token=AHTVLZ53XWBIHQXYDTNZPJL4SRWJFA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIMJTGA2TQNJZGI2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJNLQOJPWG33NNVSW45C7N5YGK3S7MNWGSY3L#issuecomment-4130585924>
@alexcani <https://github.com/alexcani>
Hi! I reviewed the PR. Appears to me to be one of the best, well-done PRs
I've reviewed on this project. It is enjoyable to see changes done to nice
little details and across all levels. I have no experience with coroutines,
so I had to do the research first. Still, given lack of experience, I don't
feel 100% confident about all the details of good coroutines design. But
given the quality of your work, I think I can trust you.
However, I did some deeper reflection about things like overall design,
thread coordination, exception handling, etc. -- and when I tried to think
about them out-of-the-box, I got back to your solution, understanding your
motives. Again, I see you've put an aspect of quality and craftsmanship to
your work, and this is something I highly appreciate.
One thing I was asking myself was -- in AwaitableData template, could we
simply use std::variant> for both the result and the exception, instead
of std::optional for one and the other? I think that
- would idiomatically be more self-revealing that only one of those is
available at a time, would transitively would eliminate potential confusion
of a reader about combinations resulting from having two std::optionals
(like can it be that both are set at the same time? Or can it be that both
are unset?), and
- would need only one member instead of always two, making it simpler,
- with no extra performance penalty.
I've already prepared a local patch for that. It works nicely. I can push
that to your branch if you're okay with that for you to review. Let me
know. In the code I touched, I also changed the formatting a bit to stay
consistent with the surrounding code.
Regarding code style, yes, clang-format would be great. I have that on my
TODO list.
Regarding copyright, all fine with me.
Regarding API, yes, it is currently C++17 compatible. Would be great to
use feature macros to have this feature conditionally available.
Regarding Awaitable public c-tor, I don't have a strong opinion, but for
consistency with other similar solutions in the library, I would slightly
tend towards private c-tor and befriending the relevant classes. It's
already done this way in other places in the library. Additionally, I would
add assert(data_ != nullptr); in the c-tor body to express the invariant
of always non-null ptr clearly.
Tests LGTM.
Clang-tidy job is failing -- shall I address the reported issues, or will
you?
I'm glad you've enjoyed using the library so far. That's was my ultimate
design goal, and I've put my best into it.
—
Reply to this email directly, view it on GitHub
<#527?email_source=notifications&email_token=AHTVLZ53XWBIHQXYDTNZPJL4SRWJFA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIMJTGA2TQNJZGI2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJNLQOJPWG33NNVSW45C7N5YGK3S7MNWGSY3L#issuecomment-4130585924>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AHTVLZZ3T3GNCLK7WP6TW5T4SRWJFAVCNFSM6AAAAACWDPIINKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DCMZQGU4DKOJSGQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
OK, I'll commit my patch. And no problem -- if I find time then I may do the adjustments myself. If not, you can do it when you are back. Enjoy your vacation. |
C++ 20 introduced coroutine support to the language, enabling writing asynchronous code using the
co_await,co_returnandco_yieldoperators. Similar to other languages, coroutines allow functions to suspend execution at certain points and later resume from where they left off, without blocking the calling thread.Coroutines are a natural fit for any sort of IO-bound asynchronous operations, such as D-Bus communication.
This PR introduces native coroutine support in sdbus-c++ by the means of the
Awaitable<T>class, a type that implements the C++20 awaitable protocol, allowing D-Bus method calls to be awaited in coroutines.Summary of the changes:
Core library
Awaitable<T>type that can beco_awaited, which suspends a running coroutine. When the result or error of a method call arrives, the coroutine is resumed and the result/error is returned.Implementation is done in a thread-safe way, meaning there are no race conditions between the awaitable object returned by asynchronous calls and the callback invoked by the event loop upon arrival of a message. Naturally, it works in the single-threaded scenario as well, where the connection event loop may be driven externally and/or integrated in a full fledged coroutine runtime.
callMethodAsync()overloads acceptingwith_awaitable_ttag and returningAwaitable<MethodReply>.getResultAsAwaitablemethods in the relevant high-level API helpers, covering methods and properties. StandardInterfaces.h classes have also been updated to expose awaitable-based methods.Codegen
org.freedesktop.DBus.Method.Async.ClientImplandorg.freedesktop.DBus.Property.[Get/Set].Async.ClientImplannotations.