Skip to content

Add socom as SOME/IP abstraction#16

Open
lurtz wants to merge 43 commits intoeclipse-score:mainfrom
elektrobit-contrib:add-socom-as-someip-abstraction
Open

Add socom as SOME/IP abstraction#16
lurtz wants to merge 43 commits intoeclipse-score:mainfrom
elektrobit-contrib:add-socom-as-someip-abstraction

Conversation

@lurtz
Copy link
Contributor

@lurtz lurtz commented Jan 23, 2026

socom is a high level SOME/IP abstraction which treats payloads as binary data. It has a plugin mechanism to support different underlying transport implementations (IPC, SOME/IP).

The current code still supports 1:n connections for events. We assume that we only need 1:1 and can simplify the code and introduce zero copy. However before doing that I would more thoroughly check our use cases.

Potentials for simplifications

  • the code supports concurrent use
    • getting rid of this will get rid of some mutexes and temporary copies
    • place restriction that registration of bridges is the first thing to do and are never allowed to be removed. The API should in best case enforce this usage
  • 1:n server:client relationships
    • getting rid of it will at least simplify Server_connector code
  • decide on needed Runtime::subscribe_find_service() functions
  • callback guarantees
    • At the moment it is guaranteed that callbacks given at construction of Server or Client_connector, will not be called anymore, after their corresponding destructors have finished. This in conjunction with concurrency caused some complexity. If we accept this is ok to happen, simplifications are again possible. The same should apply to callbacks given to Runtime::subscribe_find_service()

TODO

  • fix sanitizer findings
  • improve and split new test
  • legacy subscribe_find_service() tests might still be needed

socom is a high level SOME/IP abstraction which treats payloads as
binary data. It has a plugin mechanism to support different underlying
transport implementations (IPC, SOME/IP).
@github-actions
Copy link

github-actions bot commented Jan 23, 2026

License Check Results

🚀 The license check job ran with the Bazel command:

bazel run //:license-check

Status: ⚠️ Needs Review

Click to expand output
[License Check Output]
2026/02/17 12:23:19 Downloading https://releases.bazel.build/8.3.0/release/bazel-8.3.0-linux-x86_64...
Extracting Bazel installation...
Starting local Bazel server (8.3.0) and connecting to it...
INFO: Invocation ID: ad63b74a-7f8e-4a67-b2d2-7fc3816e41cb
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
WARNING: For repository 'googletest', the root module requires module version googletest@1.17.0, but got googletest@1.17.0.bcr.2 in the resolved dependency graph. Please update the version in your MODULE.bazel or set --check_direct_dependencies=off
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Loading: 
Loading: 5 packages loaded
Loading: 5 packages loaded
    currently loading: 
Loading: 5 packages loaded
    currently loading: 
Loading: 5 packages loaded
    currently loading: 
Loading: 5 packages loaded
    currently loading: 
Loading: 5 packages loaded
    currently loading: 
Analyzing: target //:license-check (6 packages loaded, 0 targets configured)
Analyzing: target //:license-check (6 packages loaded, 0 targets configured)

Analyzing: target //:license-check (23 packages loaded, 9 targets configured)

Analyzing: target //:license-check (92 packages loaded, 9 targets configured)

Analyzing: target //:license-check (98 packages loaded, 9 targets configured)

Analyzing: target //:license-check (130 packages loaded, 236 targets configured)

Analyzing: target //:license-check (170 packages loaded, 3032 targets configured)

Analyzing: target //:license-check (171 packages loaded, 5984 targets configured)

Analyzing: target //:license-check (177 packages loaded, 7638 targets configured)

Analyzing: target //:license-check (177 packages loaded, 7638 targets configured)

Analyzing: target //:license-check (177 packages loaded, 7638 targets configured)

Analyzing: target //:license-check (177 packages loaded, 7638 targets configured)

Analyzing: target //:license-check (180 packages loaded, 9645 targets configured)

Analyzing: target //:license-check (182 packages loaded, 9889 targets configured)

Analyzing: target //:license-check (182 packages loaded, 9889 targets configured)

Analyzing: target //:license-check (182 packages loaded, 9889 targets configured)

Analyzing: target //:license-check (182 packages loaded, 9889 targets configured)

Analyzing: target //:license-check (182 packages loaded, 9889 targets configured)

Analyzing: target //:license-check (183 packages loaded, 11815 targets configured)

INFO: Analyzed target //:license-check (183 packages loaded, 11824 targets configured).
[9 / 13] JavaToolchainCompileClasses external/rules_java+/toolchains/platformclasspath_classes; 0s disk-cache, processwrapper-sandbox ... (2 actions, 1 running)
INFO: From Generating Dash formatted dependency file ...:
INFO: Successfully converted 2 packages from Cargo.lock to bazel-out/k8-fastbuild/bin/formatted.txt
[12 / 13] [Prepa] Building license.check.license_check.jar ()
INFO: Found 1 target...
Target //:license.check.license_check up-to-date:
  bazel-bin/license.check.license_check
  bazel-bin/license.check.license_check.jar
INFO: Elapsed time: 225.932s, Critical Path: 2.63s
INFO: 13 processes: 9 internal, 3 processwrapper-sandbox, 1 worker.
INFO: Build completed successfully, 13 total actions
INFO: Running command line: bazel-bin/license.check.license_check ./formatted.txt <args omitted>
usage: org.eclipse.dash.licenses.cli.Main [-batch <int>] [-cd <url>]
       [-confidence <int>] [-ef <url>] [-excludeSources <sources>] [-help] [-lic
       <url>] [-project <shortname>] [-repo <url>] [-review] [-summary <file>]
       [-timeout <seconds>] [-token <token>]

Copy link
Contributor Author

@lurtz lurtz left a comment

Choose a reason for hiding this comment

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

These are the changes proposed for zero copy

@github-actions
Copy link

The created documentation from the pull request is available at: docu-html

Copy link
Contributor

@NEOatNHNG NEOatNHNG left a comment

Choose a reason for hiding this comment

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

First glimpse before the weekend.


[[nodiscard]]
virtual Result<std::unique_ptr<Writable_payload>> allocate_method_payload(
Method_id /* method_id */) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

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

As we already discussed in the meeting I think it would make sense to have the events/methods as their individual interface class so that you can call Allocate()/Call()/Send() on the element without always having to specify the IDs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will try to create something and use inspiration from mw::com.

Copy link
Contributor Author

@lurtz lurtz Jan 26, 2026

Choose a reason for hiding this comment

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

This is one proposal for the Client_connector: 71dc2b1

This API I like more than what was designed at mw::com or how it was before at socom, because it uses RAII. I will now craft something which looks more like GenericProxy

This is an implementation similar to mw::com::GenericProxy: ad77a62

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have a look at the state of a34dc64

That is the final commit of the API refactoring. I think the API is cleaner and better structured and still offers the same functionality. But compared to the previous state both Client_connector and Server_connector need more member variables. Maybe I can bring this down with some refactoring.

Also the get_events() and get_methods() functions create a new std::vector. I do not know if that is good.

/// \param client_id ID of the event.
/// \param mode Mode of the event.
/// \return Void in case of successful operation, otherwise an error.
virtual Result<Blank> subscribe_event(Event_id client_id, Event_mode mode) const noexcept = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

On the client side does it make sense to specify the mode? Isn't the mode determined by the server side?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The mode is supposed to distinguish Events and Fields.Event_mode::update is normal events, where you might not have an initial value and Event_mode::update_and_initial_value is for fields, where you should immediately get you event update callback called after subscription.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, understood. But does it make sense to have that on the client side? Because normally the server needs to indicate whether the event has an initial value, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you think about moving it to the configuration?

https://github.com/elektrobit-contrib/eclipse-score_inc_someip_gateway/blob/8c21e9e185ebf4a059bb1ea910b215d290af26f9/src/socom/test_unit/smoke_test.cpp#L60-L61

There you can only specify the number of events. Instead of a number, this could be a list of Event_mode::update and Event_mode::update_and_initial_value.

The structure is not required by the Client_connector and it can work with a subset of this struct.

namespace score::socom {

/// \brief Error conditions when using Client_connector.
enum class Error : std::uint8_t {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should that be adjusted to the general ErrorCode pattern?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I implemented a not completely cleaned up version (to keep the diff short) of this: fc0376c

I am hot 100% happy with the result. Inspired from Rust I wanted to make it very clear that all possible values specified in an error enum are actually possibly to return. This is the reason why there are specialized enums with subsets or other values. Otherwise I would have had only a single error enum for socom.

namespace score::socom {

/// \brief Alias for an event ID.
using Event_id = std::uint16_t;
Copy link
Contributor

Choose a reason for hiding this comment

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

If we keep the concrete IDs out of the definitions we would have a generic service API that we could maybe also use for other network protocol stacks (e.g. DDS?)

If we use something like CreateRemoteEvent(std::string_view event_name) to create a specific object then ID to be used could be fetched from the config.

The upper layer code still has to be changed to serialize the payload differently but at least we could re-use the same API.

Copy link
Contributor Author

@lurtz lurtz Jan 26, 2026

Choose a reason for hiding this comment

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

The Event_id is not the SOME/IP Event id. It is a consecutive integer range starting from 0. Thus the first SOME/IP event of a service interface is mapped to Event_id{0} and so on. This implies all parties agree on the ordering of events and if they do not, we might need something like you proposed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, then I think we need a name because having an index seems too error prone in my experience.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have to find a design, where either string comparisons are cheap or we do as less as possible.

Also keep in mind that my intention was not to read configuration files within the socom code. It is the code using socom, which is supposed to do that.

My first idea would be that each side passes a list of strings and socom::Runtime creates a mapping. However this way we will end up with n^2 string comparisons when connecting client and server connector per service. That might cause too much overhead at system startup.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you mean. If we really go the direction of sending the configuration over to the other end when connecting then we probably won't have a mismatch. Then we can also go back to the old implementation of dispatching everything by the ID. That way we would have to do the dispatch to the different service components in the RemoteServiceInstance, but I guess that is OK and would also save us some callback objects.

Copy link
Contributor

@NEOatNHNG NEOatNHNG left a comment

Choose a reason for hiding this comment

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

Reviewed the design and most of the API which seems fine so far. What I have not looked into yet is the implementation. Does it make sense to have a look into the implementation yet or do we want to simplify first?

Comment on lines 25 to 26
Available --> Not_available: Client_connector::\nsubscribe_event()
Available --> Event_subscribed: Client_connector::\nsubscribe_event()
Copy link
Contributor

Choose a reason for hiding this comment

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

Under which condition do we switch to Event_subscribed or Not_available? When subscribe_event() returns an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This looks wrong to me. When we have an Enabled_server_connector the only error, which can happen on the Client_connector::subscribe_event() side is using an out of range id. However this will not disconnect both and instead just return an error.

Comment on lines 19 to 20
state Event_subscribed
state Event_subscribed_acknowledged
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a bit confusing. Maybe rename like this?

Suggested change
state Event_subscribed
state Event_subscribed_acknowledged
state Event_subscription_pending
state Event_subscribed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The whole sequence diagram looks like it needs improvement. I will do my best

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the confusion comes from the fact that events can be received in both states. The acknowledged state is optional.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, so the receival of the ACK switches to the Event_subscribed_acknowledged state but has no further effect? Should we then remove it?
On the protocol level the ACK is needed so that the subscription request can be resent in case it is lost on the remote end, but on the local machine we probably don't need the application to know about this because it shouldn't need to resend the subscription request, correct?
If we don't need it I think we should remove it to keep it simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess mw::com does not have similar API either. I remove it

Event_subscribed --> Not_available: Client_connector::Callbacks::\non_service_state_change\n(!Service_state::available)

Event_subscribed_acknowledged --> Available: Client_connector::\nunsubscribe_event()
Event_subscribed_acknowledged --> Event_subscribed: Client_connector::Callbacks::\non_event_subscription_status_change\n(Event_state::unsubscribed)
Copy link
Contributor

Choose a reason for hiding this comment

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

How to get from Event_subscribed to Event_subscribed_acknowledged again if we got there via this transition? Is the subscription retried automatically (i.e. responsibility of the server connector)?

Copy link
Contributor Author

@lurtz lurtz Feb 3, 2026

Choose a reason for hiding this comment

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

IMHO two state machines are mixed here. The one acknowledging event subscriptions is not required to be used: https://github.com/elektrobit-contrib/eclipse-score_inc_someip_gateway/blob/a34dc64af9901e8e883c730fc8cdcadf8bfe921d/src/socom/include/score/socom/server_connector.hpp#L198-L206

Thus event transmission works independently of calling Enabled_server_connector::set_subscription_state(). Which also means no Client_connector will be connected or disconnected when this function is called.

As far as I can see the Client_connector will get unsubscribed from all events offered by an Enabled_server_connector, when the Enabled_server_connector gets destroyed or converted to Disabled_server_connector. In both cases events are not resubscribed automatically once we get the Enabled_server_connector back and the user of the Client_connector has to do it explicitly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forget my previous answer.

How to get from Event_subscribed to Event_subscribed_acknowledged again if we got there via this transition?

This state transition is toggled via Enabled_server_connector::set_subscription_state()

switch to Not_available happens when Callback gets called
@lurtz
Copy link
Contributor Author

lurtz commented Feb 3, 2026

Reviewed the design and most of the API which seems fine so far. What I have not looked into yet is the implementation. Does it make sense to have a look into the implementation yet or do we want to simplify first?

IMHO we should settle on the API first. We can peek into the implementation to make sure that the API is not expensive.

@lurtz lurtz force-pushed the add-socom-as-someip-abstraction branch from 24fea57 to 5d8e581 Compare February 4, 2026 12:48
lurtz and others added 3 commits February 5, 2026 09:58
The socom tests have a very good coverage and also do stress tests to
detect race conditions in the implementation.

They are written as black box tests and make no assumption of the inner
architecture of socom.
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.

2 participants