gdbstub icon indicating copy to clipboard operation
gdbstub copied to clipboard

Async traits

Open SidongYang opened this issue 1 year ago • 6 comments

Hi, I'm using gdbstub crate very well. I'm writing code for gdbserver that controls embedded system in kernel. Preferred action is to write/read device file in implementing traits like SingleThreadSingleStep::step() or SwBreakpoint::add_sw_breakpoint(). I want to use async file write in these functions but they are not async callback. Is there any good way to call async function in these traits? or it needs some patch?

SidongYang avatar Nov 28 '24 14:11 SidongYang

The short answer is that, unfortunately, gdbstub's current feature-level IDET traits are all sync, so unless you're willing to use block_on, there's no good way to call an async function from those handlers.

For context: the last time the question of async in gdbstub came up was in the context of the top-level GdbStub API and the Connection trait, which used to force users to block / periodically poll in order to handle certain aspects of the GDB RSP (see #36). That limitation was worked around in 0.6 with the introduction of the state-machine API, which tweaked the APIs to allow users to manually "pump" the GDB state machine with incoming data, thereby making it easier to integrate into whatever existing async infrastructure their project was using.

This solved the async transport question, but it didn't solve the async handler question, like you're running into.

It seems most folks don't really mind this limitation, but its certainly a limitation nonetheless. The async Rust ecosystem and language support has evolved a lot in the years since gdbstub began development, so it may be time to re-evaluate the possibility of leveraging async / await in gdbstub.

The questions that immediately come to mind here are:

  • At the language level, how easy would it be to use async traits alongside gdbstub's IDET-based API?
  • At the integration level, what would the binary size impact be of using async traits?
    • Would there need to be two parallel GdbStub core implementations, so non-async users don't pay the binary-size tax of having the library be based on async?
  • At the gdbstub codebase level, what would be the best way to support both async and non-async families of traits?

I'm happy to use this issue as a tracking issue for folks to express interest in the feature, as well as explore / answer some of the questions I've posed above.

I'm not sure when I'll have cycles to do some of this work myself, but if you / other folks are interested in helping explore the design-space here, I could certainly try to find some more time and motivation to help work through some of these questions, and/or review some PRs.

daniel5151 avatar Nov 28 '24 18:11 daniel5151

It might seem simplistic, but one potential solution could be duplicating all the code handling incoming data into an asynchronous function.

SidongYang avatar Dec 08 '24 03:12 SidongYang

It might seem simplistic, but one potential solution could be duplicating all the code handling incoming data into an asynchronous function.

Yep, that's certainly an option, and one of the things I was thinking about when I posited: "At the gdbstub codebase level, what would be the best way to support both async and non-async families of traits?"

Note that whatever solution we come up with here needs to adhere to one "non-negotiable" property: gdbstub's codebase cannot manually maintain two parallel API sets - one sync, and one async. That is to say: even if that is the end-user API has two entrypoints - one sync, and one async - the codebase itself must be written such that all APIs only have a source-of-truth, possibly leveraging some kind of macro / code-generation mechanism to have the build system automatically generate the two API variants.

For a time, it seemed like Rust might gain some kind of keyword generics mechanism, to allow a single codebase to expose both sync and async APIs, but to my knowledge, that effort is stalling, and/or may not be the direction the language decides to go in.

In the absence of that sort of language level mechanism, I'm not sure what the current "state of the art" is for supporting that sort of parallel sync vs. async API.


There's also the possibility of swapping the entire gdbstub API from blocking to async, and simply providing some block_on-based helper methods for users that only wish to use GdbStub in a blocking manner. This would be a breaking change, and necessitate a new release, but not totally out of the question.

That said: this solution must conform to one critical property for it to be viable: it cannot bloat the binary size of minimal no_std integrations. This is a key condition, and one that my gut says will not be the case, given the current state of rustc and llvm optimizations.

If you or someone else would like to experiment with this approach, and profile what the impact looks like on codegen, that would be incredibly useful. If it turns out my gut was wrong, and this approach has minimal codegen impact, then I would be more than happy to go with this approach, as it maintains the "single source-of-truth" property without requiring any macros / build-system shenanigans.

daniel5151 avatar Dec 27 '24 23:12 daniel5151

I think using maybe_async is good option for this problem. It doesn't affacts original sync codegen and it supports async code. I submitted an experimental PR for this https://github.com/daniel5151/gdbstub/pull/161

SidongYang avatar Jan 02 '25 02:01 SidongYang

Thanks for sending in the PR!

For anyone stumbling across this issue: lets move any further discussion related to maybe_async to #161 for the time being. Once we have a better idea of whether maybe_async is a viable approach (or not), we can come back to this issue and summarize our findings.

In the meantime, I'll leave this issue open for further discussion on the broader issue, and any other considerations / comments / ideas folks may have.

daniel5151 avatar Jan 05 '25 21:01 daniel5151

Hopefully I'll find some time to rephrase / summarize the discussion on #161, but for now - please see https://github.com/daniel5151/gdbstub/pull/161#issuecomment-2614152771 for my thoughts on both how gdbstub might want to tackle async trait support.

daniel5151 avatar Jan 30 '25 22:01 daniel5151