-
-
Notifications
You must be signed in to change notification settings - Fork 51
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
use maybe_async that supports async traits for breakpoint #161
Conversation
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.
Thanks for sending in this PR!
At first blush, maybe-async
certainly seems to tick many of the boxes I'm interested in, particularly regarding having a single unified codebase for both sync and async code.
There are, however, a few things that concern me...
Given that async-trait
requires boxing futures... I would like to better understand and profile how that extra indirection affects the dead-code-elimination guarantees that the IDET pattern otherwise guarantees in release mode.
At minimum, as a next-step in this exploration: can you sprinkle some maybe-async
annotations into the armv4t
code, such that we can do some side-by-side comparisons between the sync and async APIs via the in-tree /scripts/test_dead_code_elim.sh
script?
Alternatively - it seems that maybe_async(AFIT)
exists... but using it would be a bit annoying, and when gdbstub
is configured in async
mode, all current IDET methods would need to return impl Trait
instead of dyn Trait
, since Rust doesn't have native dyn async traits yet. Maybe that sort of transformation could be done automatically by forking / extending maybe_async
... unlear.
Oh, and of course - boxing futures isn't particularly no_std
friendly... and it would certainly be interesting to have gdbstub
's async APIs play nice with no_std
projects using async infrastructure (e.g: via embassy). I wouldn't say that "perfect" async no_std
support is a hard requirement for any v0 of async gdbstub
... but I would like to at least understand what sorts of gaps / implications will result once we introduce async stuff into the gdbstub
codebase.
P.S: don't worry about those clippy lints. If you rebase your PR on top of latest master
, I've gone ahead and fixed those.
@@ -35,6 +37,7 @@ std = ["alloc"] | |||
trace-pkt = ["alloc"] | |||
paranoid_unsafe = [] | |||
core_error = [] | |||
sync = ["maybe-async/is_sync"] |
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.
Whether or not gdbstub
should present a sync vs. async API by default is an interesting question. I suppose that's a judgement call we can make later, if we decide that maybe-async
is the basis of the approach we take.
@@ -154,7 +155,8 @@ impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { | |||
/// etc...) you will need to interface with the underlying | |||
/// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) API | |||
/// directly. | |||
pub fn run_blocking<E>( | |||
#[maybe_async] | |||
pub async fn run_blocking<E>( |
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 know this is just experimental code... but having a run_blocking
method that is actually async
seems funky. I suppose we'll have to think a bit harder about how to model / rename this API if we go down this route
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.
Agreed, It needs to be renamed
b81abcd
to
70063c9
Compare
70063c9
to
a91ab2e
Compare
It seems that CI failed because in compiling no std example, it cannot find |
@daniel5151 I don't want to break default features for compatibility. but I can't find good way to choose sync traits as default. |
Thanks for iterating here! Now that the armv4t example is working alongside async, lets run some of those experiments I mentioned above, regarding dead code elimination. i.e: comment out the There are other, arguably more interesting experiments that I'd like to run before committing to this approach. Namely: I'd like to explore the use of More ambitiously, as I mull things over even more, I'm wondering if it might make sense to just go all-in on All that is to say - while it's really neat to see that Let me know if you think you can help dig into some of these experiments here. If not, I'll try to allocate some time at some point in the next few weeks to see if I can play around here. There are a few different
Indeed, I think there's no good way to get around this issue when using |
I agree that AFIT options is best for using maybe_async. I pushed a commit that use |
32613d3
to
9fea11d
Compare
Hey, really sorry for the lack of activity here. A ton of personal and work stuff has come up over the past few weeks, and I've yet to find a moment of focus time to give feedback here... My apologies :( I'm going to try and set aside some time this weekend to give this a review, and collect my thoughts on this PR. |
I've cloned the branch locally, and when I go to run
...which totally makes sense, given that we've switched to using non-allocating monomorphized AFIT, instead of allocating dyn-based async_trait. Working around these errors will require diving deeper into the nuts-and-bolts logistics of what it'd take to migrate
As we keep digging into this space, I am starting to suspect that introducing Namely, Instead, the paradigm that I've been mulling over which could serve as the "next generation" of What is Here's a sketch of what I mean by this: // Instead of having a method that GdbStub calls, as we do today...
impl SingleThreadResume for MyTarget {
fn resume(&mut self, signal: Option<Signal>) -> Result<(), Self::Error> { todo!() }
}
// lift the target handlers _outside_ GdbStub's call-stack, having users drive
// GdbStub's state machine themselves
async fn my_gdbstub_loop() {
let mut target = MyTarget::new().await;
let mut conn = MyConn::new().await;
let mut gdb = GdbStubState::new(...)?; // no Connect, no Target - GdbStub only contains protocol state
loop {
gdb = match gdb {
// still have the same top-level "lifetime" methods as the current state machine API
GdbStubState::Idle(gdb) => { gdb.incoming_data(conn.get_byte().await) },
// introduce new target-handler states
GdbStubState::OnResume(gdb, signal) => { target.handle_resume(signal).await; gdb.handled_resume() },
// ...and so on, with whatever other states you opt-in to handling
// and also have new error states
GdbStubState::OnError(gdb, e) => { /* handle e, maybe recover */ }
// with a generic "fallback" case, to account for unimplemented protocol extensions
_ => gdb.on_unsupported(),
}
}
} As you can see, by fully leaning into the state machine model that was originally introduced in #88, it becomes trivial to do async operations, without having to plumb through Moreover, it should be entirely possible to write a "user-friendly" wrapper around this state machine architecture, such that existing users of Of course, this is just a sketch, and there are still many unanswered questions I have with this new architecture. Notably: how do we preserve the dead-code-elimination properties that the IDET technique guarantees? i.e: how can we allow users to only implement certain All that is to say - I really appreciate you digging in here, and running some of these experiences. That said - I'm not sure I'll be able to definitively answer the question of "what is the best I think we should close this PR for now, and I'll summarize some of these ideas I've sketched out in a more focused manner, either on the #159 issue itself, or (more likely), via a new dedicated tracking issue for evaluating the feasibility and challenges inherent behind this new |
Thanks for very detailed explanation. It helps me to understand async rust deeper. Actually I didn't understood 100% for now. So I need to think deeply about this.
Agreed, Anyway this PR is just experimental and just for idea. I'll close this PR with this comment. Also I would like to continue discussing on the issue #159 |
Description
This PR is exprimental codes that uses
maybe_async
for supporting fully async traits. Adding simplemaybe_async
macro for trait and it generates async code whenis_sync
feature is disabled. For this there is new featureis_sync
for gdbstub which is default feature. So it doesn't affacts original sync code.issue: #159
API Stability
Checklist
rustdoc
formatting looks good (viacargo doc
)examples/armv4t
withRUST_LOG=trace
+ any relevant GDB output under the "Validation" section below./example_no_std/check_size.sh
before/after changes under the "Validation" section belowexamples/armv4t
./example_no_std/check_size.sh
)Arch
implementationValidation