Skip to content

created initial rust based design draft for mw::diag#78

Draft
MonikaKumari26 wants to merge 1 commit intoeclipse-opensovd:mainfrom
MonikaKumari26:rust_abstract_layer_api_for_user_code
Draft

created initial rust based design draft for mw::diag#78
MonikaKumari26 wants to merge 1 commit intoeclipse-opensovd:mainfrom
MonikaKumari26:rust_abstract_layer_api_for_user_code

Conversation

@MonikaKumari26
Copy link

  • I have tested my changes locally
  • I have added or updated documentation
  • I have linked related issues or discussions
  • I have added or updated tests

Related

Notes for Reviewers

{abstract} +unlock(&mut self) -> Result<()>
{abstract} +kind(&self) -> DiagnosticEntityKind
{abstract} +supported_modes(&self) -> Result<Vec<DiagnosticEntityMode>>
{abstract} +apply_mode(&mut self, mode_id: &str, mode_value: &str, expiration_timeout: Option<Duration>) -> Result<()>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we were discussing async APIs, maybe it would make sense here to use one

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to be async since applying a diagnostic mode might take time. supported_modes() could be also async if querying supported modes requires reading from vehicle systems.

Copy link
Contributor

@lh-sag lh-sag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MonikaKumari26 thanks for your contribution!

@MonikaKumari26 MonikaKumari26 force-pushed the rust_abstract_layer_api_for_user_code branch 2 times, most recently from a87978f to e9e5e95 Compare February 5, 2026 11:38
@lh-sag lh-sag self-requested a review February 5, 2026 19:47
@lh-sag
Copy link
Contributor

lh-sag commented Feb 5, 2026

@MonikaKumari26 I suggest to move this PR out of draft status to get more feedback.

@MonikaKumari26 MonikaKumari26 force-pushed the rust_abstract_layer_api_for_user_code branch from e9e5e95 to 82693a9 Compare February 6, 2026 12:29
Copy link

@darkwisebear darkwisebear left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not done, but maybe these comments are helpful still. One general comment: This library allocates a lot. We need to see whether we can cut back on allocations (or avoid them somehow?) and we need to introduce an Allocator trait, as the canonical one from std isn't stabilized yet.

Comment on lines +233 to +236
+with_read_resource<T: ReadOnlyDataResource + Send + Sync + 'static>(self, id: impl Into<String>, resource: T) -> Self
+with_write_resource<T: WritableDataResource + Send + Sync + 'static>(self, id: impl Into<String>, resource: T) -> Self
+with_data_resource<T: DataResource + Send + Sync + 'static>(self, id: impl Into<String>, resource: T) -> Self
+with_operation<T: Operation + Send + Sync + 'static>(self, id: impl Into<String>, operation: T) -> Self

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for these types to be Sync?

Copy link
Author

@MonikaKumari26 MonikaKumari26 Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that these references must be Sync to allow concurrent read access from multiple threads, ensuring safe shared diagnostic operations and no compiler errors when shared via &T.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that'd only be relevant if I wanted to execute the same operation concurrently. Is that really the case? Imo executing an operation should be exclusive, which means that T needs to be Send (so that an instance of T can be executed in different threads), but not Sync (because no concurrent execution is required).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the concurrency constraints, I initially used uniform Send + Sync requirement for simplicity, but I have now refined this to be more precise based on resource behavior:

  1. Operation Trait: Retained Send + Sync. Since it exposes both &self and &mut self methods, Sync is required to allow concurrent status queries via immutable references.
  2. Write-only resources/services, RoutineControl and UdsService to be Send only, as they don't benefit from concurrent access.
  3. Read-only Resources: Retained Send + Sync to explicitly support concurrent reads.

Design may need further adjustments if these Sync requirements conflict with specific downstream use cases.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&self / &mut self are static ownership properties that help the compiler to find out whether it's possible to allow for modifications of the instance state in a certain context (imo &mut should have been called &unique).

In conjunction with concurrency traits, it helps the compiler to know whether copying a reference across thread boundaries preserves these properties. However, in case of e.g. Operation types, even if they're Sync, the method signature of e.g. execute requires &mut self, so you statically demand a unique ownership. This either requires a mutex or something similar to go from shared to unique ownership if this exclusivity is enforced at runtime, or even a static unique ownership, which is hard to obtain unless you completely handle all diag events in a single thread. But since you already demand unique ownership, requiring the type to be Sync is asked too much as you do not need to synchronize anyway if ownership is already unique.

The bottom line for me is that if we have &mut self methods, requiring Sync doesn't help at all. Sync only makes sense if most / all methods are &self so that you can call them from multiple threads truly concurrently. Deciding this means to think whether synchronization shall be done by all implementors of Operation and the like (which makes these types Sync, but allows for fine grained synchronization if required), or whether the framework will take care, which makes implementing the diagnostic structs easier, but the synchronization will be more coarse grained.

@MonikaKumari26 MonikaKumari26 force-pushed the rust_abstract_layer_api_for_user_code branch from 82693a9 to 887af25 Compare February 11, 2026 08:56
@MonikaKumari26 MonikaKumari26 force-pushed the rust_abstract_layer_api_for_user_code branch from 887af25 to 7825f43 Compare February 13, 2026 12:42
Copy link
Contributor

@lh-sag lh-sag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MonikaKumari26 if you want broader feedback I would advise you to move this PR from draft to final.

**Important**: This design is intended for automated code generation. Key constraints:

- **Rust Edition**: 2021+ (minimum required)
- **Async Strategy**: All trait methods are currently **synchronous**. While shown in design for API clarity, the current implementation does not use async. Future versions may add async support using `#[async_trait]` macro.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO it is easier to have async fn first and support blocking on top. What is the rational to start with sync first?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the original statement in the document is too generic. We need to look at the methods one by one and determine their nature:

Operation

Most methods query state; these methods are nonblocking and there is imo no reason to make them async, not even in a second step, as such data acquisition should not require any long running activities. The same is true for the execute method: This one also isn't supposed to block. If it does need to do something that takes longer, it is supposed to spawn a concurrent task or thread and return with a pending state.

The only notable exception in my eyes is stop, because I could imagine that stopping a task might incur some waiting. I'd need to check the SOVD spec whether or not we'd need to delay the response of this method until the task really stopped.

ReadableDataResource

I think it makes sense to make the get call async, as it is absolutely thinkable that data acquisition will be done asynchronously by the implementation.

WritableDataResource

Here, the same argumentation as for the execute call holds, unless it is required by the spec to also tell whether setting the data actually happened (aka a positive response not just confirms data reception but also confirms proper changing of the inner state). In that case, the put method also needs to be async.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your implications about the system might fit for yours but we need an async interface for ours. It is just a matter of how we implement it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. Let's take Operation: If you opt for an API design (and that's how it looks) where you map SOVD requests 1:1 to API calls, the resulting API and the way how you implement the trait should give you an idea about what color your function is.

If you disagree with my assessments, please give your view of that particular method and why it should or shouldn't be async, or you also mention why the suggested API design philosophy (have a thin layer above the REST calls) is not the correct one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. And our operations/API is fully async this does not mean write will work differently as you suggested but our core is async. This is completely orthogonal to SOVD.

Please note that I do not enforce either way sync or async, just that we have an async interface as our system underneath work differently than yours.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo the API should already be usable for you as well without having to jump through hoops; either we will have a truly async method anyway (e.g. get of ReadableDataResource), or the methods are ordinary ones without suspension points, but these shall be (see my comment above) nonblocking, so that they can also be called in an async context. So even if you are fully async, this shouldn't prevent you from being able to implement this API.

On a side note, we shouldn't actually talk about "your system" or "our system". In the end we want to build something that can be used in many different contexts, and if there are contexts where the API is not a good fit, I'd need to understand what exactly isn't fitting so that we can develop a common solution that fits.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a side note, we shouldn't actually talk about "your system" or "our system". In the end we want to build something that can be used in many different contexts, and if there are contexts where the API is not a good fit, I'd need to understand what exactly isn't fitting so that we can develop a common solution that fits.

We do agree here. But since our systems differs we are biased, I doubt we would have argued if this is not the case. Therefore my initial remark was whether we have a fully sync and async interface.

Where we differ is having a mixed one e.g. I do not see why for example Operation::status should not be colored async. The question about async/sync interface is independent of how SOVD works.

I will join next weeks UDS2SOVD meeting in case you want to discuss this outside of this PR.

Comment on lines +344 to +354
interface Operation <<trait>> {
API definition for SOVD operations

.. standard SOVD capabilities ..
{abstract} +async info(&self, request: DiagnosticRequest) -> Result<OperationInfoReply>
{abstract} +async status(&self, request: DiagnosticRequest) -> Result<OperationStatusReply>
{abstract} +async execute(&mut self, request: DiagnosticRequest) -> Result<ExecuteOperationReply>
{abstract} +async resume(&mut self, request: DiagnosticRequest) -> Result<ExecuteOperationReply>
{abstract} +async reset(&mut self, request: DiagnosticRequest) -> Result<ExecuteOperationReply>
{abstract} +async stop(&mut self, request: DiagnosticRequest) -> Result<()>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API must follow synchronous non-blocking pattern here. The implementation should avoid the overhead of a multi-threaded async runtime while also ensuring functions do not halt execution while waiting. Instead, operations should return immediately with a status (WouldBlock or InProgress), requiring the caller to poll for completion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping info() and status() as async makes sense because info may involve remote metadata fetches and status polling aligns with the REST async‑polling pattern and works naturally as async.

interface WritableDataResource <<trait>> {
API definition for writable SOVD data resources

{abstract} +async put(&mut self, request: DiagnosticRequest) -> Result<()>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write should return status immediately and long‑running operations should expose a status that callers can poll. Convert write to a synchronous, non‑blocking API that returns a status enum.

Suggested change
{abstract} +async put(&mut self, request: DiagnosticRequest) -> Result<()>
fn write(&mut self, request: DiagnosticRequest) -> Result<WriteStatus>
enum WriteStatus {
Completed, // Write finished successfully
InProgress, // Write initiated, still processing
WouldBlock, // Cannot start now, retry later
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who would consume/poll the write status?

I would also like to understand why we drop async. It can have the same semantic as you mentioned here but allows for async i/o. We will have a sync meeting tomorrow. feel free to discuss this further.

@MonikaKumari26 MonikaKumari26 force-pushed the rust_abstract_layer_api_for_user_code branch from 7825f43 to 6086c37 Compare March 3, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants