diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 631bbdda..7e045359 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,7 +22,7 @@ repos:
- id: check-executables-have-shebangs
- id: check-added-large-files
args: [--maxkb=50, --enforce-all] # increase or add git lfs if too strict
- exclude: MODULE.bazel.lock|tests/benchmarks/benchmarks_analysis.md
+ exclude: MODULE.bazel.lock|tests/benchmarks/benchmarks_analysis.md|src/socom/test/unit/runtime_tests.cpp
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: 38980559e3a605691d6579f96222c30778e5a69e # 3.0.0
diff --git a/src/socom/BUILD b/src/socom/BUILD
new file mode 100644
index 00000000..fa4baee7
--- /dev/null
+++ b/src/socom/BUILD
@@ -0,0 +1,54 @@
+# *******************************************************************************
+# Copyright (c) 2025 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Apache License Version 2.0 which is available at
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# SPDX-License-Identifier: Apache-2.0
+# *******************************************************************************
+
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
+load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
+
+filegroup(
+ name = "headers",
+ srcs = glob(include = ["include/**"]),
+)
+
+cc_library(
+ name = "socom",
+ srcs = glob(include = ["src/**"]),
+ hdrs = [":headers"],
+ defines = [
+ "WITH_SOCOM_DEADLOCK_DETECTION=1",
+ ],
+ features = COMPILER_WARNING_FEATURES,
+ includes = [
+ "include",
+ "src/socom/include",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "@score_baselibs//score/result",
+ ],
+)
+
+filegroup(
+ name = "mock_headers",
+ srcs = glob(include = ["mock/**"]),
+)
+
+cc_library(
+ name = "mock",
+ hdrs = [":mock_headers"],
+ includes = ["mock"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":socom",
+ "@googletest//:gtest_for_library",
+ ],
+)
diff --git a/src/socom/README.adoc b/src/socom/README.adoc
new file mode 100644
index 00000000..e6d8811c
--- /dev/null
+++ b/src/socom/README.adoc
@@ -0,0 +1,36 @@
+// *******************************************************************************
+// Copyright (c) 2025 Contributors to the Eclipse Foundation
+//
+// See the NOTICE file(s) distributed with this work for additional
+// information regarding copyright ownership.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Apache License Version 2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: Apache-2.0
+// *******************************************************************************
+
+= Service Oriented Communication (SOCom)
+:description: SOCom
+:docinfo: shared
+:diagramdir: models
+:imagesdir: images
+:toc: left
+:iconsdir: images
+:icons: font
+:sectnums:
+:sectnumlevels: 5
+
+The bundle service oriented communication (SOCom) implements components for client/server pattern based communication for the following communication paradigms:
+
+* remote procedure call;
+* publish/subscribe (event communication).
+
+SOCom provides interfaces which enable the implementation of service gateways (bridges).
+This allows users to independently implement service bridges for different communication protocols (e.g. IPC, SOME/IP, …).
+SOCom provides intra-process client/server communication only.
+
+== Design
+
+See <> for a high level design.
diff --git a/src/socom/doc/design/30_structural_view.adoc b/src/socom/doc/design/30_structural_view.adoc
new file mode 100644
index 00000000..530b3bc6
--- /dev/null
+++ b/src/socom/doc/design/30_structural_view.adoc
@@ -0,0 +1,81 @@
+// *******************************************************************************
+// Copyright (c) 2025 Contributors to the Eclipse Foundation
+//
+// See the NOTICE file(s) distributed with this work for additional
+// information regarding copyright ownership.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Apache License Version 2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: Apache-2.0
+// *******************************************************************************
+
+////
+Every asciidoc file in this document starts with the section level 0, i.e. a section like
+....
+= Title
+....
+
+When the file is included from another file within the document, the levels must be adjusted with the `leveloffset` attribute to the `include` directive at the point of inclusion, e.g.
+....
+include::file.adoc[leveloffset=+1]
+....
+////
+
+= Structural View
+
+The bundle service oriented communication (SOCom) implements components for client/server pattern based communication for the following communication paradigms:
+
+* remote procedure call;
+* publish/subscribe (event communication).
+
+SOCom provides interfaces which enable the implementation of service gateways (bridges).
+This allows users to independently implement service bridges for different communication protocols (e.g. IPC, SOME/IP, …).
+SOCom provides intra-process client/server communication only.
+
+.Component diagram of SOCom
+[plantuml, svg, align="center"]
+....
+include::models/component_diagram_socom.puml[]
+....
+
+.Software elements
+
+* <>;
+* <>;
+* <>;
+
+[#runtime_component]
+== Runtime
+
+The Runtime creates and connects Client_connectors and Server_connectors.
+In addition to that it has a plugin interface for adding bridges to cross IPC or network boundaries.
+The Runtime must outlive all created Client_connectors and Server_connectors.
+
+[#client_connector_component]
+== Client connector
+
+A client application owns and uses this component to join client/server pattern based service oriented communication (SOCom).
+The client connector interacts with the server connector in order perform the supported communication primitives.
+The client connector API provides the following features:
+
+* service instance state change indications
+* asynchronous remote procedure call
+
+** with method reply
+** without method reply (fire and forget)
+
+* event subscription
+
+** with optional initial value request (required for fields)
+
+* event subscription acknowledge indication
+* event update indication
+* requested event update indication
+
+[#server_connector_component]
+== Server connector
+
+The Server_connector represents a service, which is used by a Client_connector.
+It can send event updates and respond to method calls.
diff --git a/src/socom/doc/design/40_behavioral_view.adoc b/src/socom/doc/design/40_behavioral_view.adoc
new file mode 100644
index 00000000..19a4e9cf
--- /dev/null
+++ b/src/socom/doc/design/40_behavioral_view.adoc
@@ -0,0 +1,172 @@
+// *******************************************************************************
+// Copyright (c) 2025 Contributors to the Eclipse Foundation
+//
+// See the NOTICE file(s) distributed with this work for additional
+// information regarding copyright ownership.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Apache License Version 2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: Apache-2.0
+// *******************************************************************************
+
+////
+Every asciidoc file in this document starts with the section level 0, i.e. a section like
+....
+= Title
+....
+
+When the file is included from another file within the document, the levels must be adjusted with the `leveloffset` attribute to the `include` directive at the point of inclusion, e.g.
+....
+include::file.adoc[leveloffset=+1]
+....
+////
+
+= Behavioral View
+
+This component implements the following state machines:
+
+* <>;
+* <>.
+
+== Runtime
+
+The runtime is a (process internal) service instance broker.
+Client connectors and server connectors are created using the runtime (factory), which keeps track of their existence and state.
+Depending on the existence and state of the communication partners, the runtime takes care for connecting/disconnecting service interface and service instance compatible client and server connectors.
+Additionally, it provides a service instance find interface.
+
+== Server connector
+
+A server application owns and uses this component to join client/server pattern based service oriented communication (SOCom).
+The server connector interacts with the client connector in order perform the supported communication primitives.
+The server connector API provides the following features:
+
+* remote procedure called indication;
+* event subscription state changed indication:
+
+** on first subscriber;
+** on last unsubscriber.
+
+* event update request indication;
+* event mode getter (event with/without initial update);
+* update event;
+* update requested event;
+* acknowledge event subscription.
+
+== Method Communication
+
+Methods calls are routed 1:1 from client to server application.
+If a method reply is requested, the calling client is served with the related reply.
+Independent method calls share no state.
+
+.Interaction diagram: Method communication
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_method_communication.puml[]
+....
+
+== Event Communication
+
+The client subscribes to an event.
+On the subscription, SOCom informs the server using the request for events.
+If no client is subscribed to an event, the server will not send any event updates.
+
+After unsubscription of the subscriber, SOCom informs the server that an event is not subscribed anymore.
+SOCom keeps track of subscription states and related clients.
+
+.Interaction diagram: Event communication
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_event_communication.puml[]
+....
+
+== Field Notification Communication
+
+A field notification is implemented as an event with event update on subscription.
+
+NOTE: Additional behavior compared to field communication:
+since SOCom does not save the current event values, SOCom requests an event update from the server for each new subscriber.
+
+Servers answer this request with update_requested_event().
+Those updates are indicated to the client which is subscribed and has an initial event update pending.
+
+Regular update_event() calls update for the events of the subscriber and also fulfills the initial event update use-case.
+Consequently, update_event() satisfies the event update request implicitly.
+
+.Interaction diagram: Field notification communication
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_field_notification_communication.puml[]
+....
+
+== Service Gateway - find service
+
+The creation of client connectors is forwarded to bridges as request_service() calls.
+Service bridges look up the required service instance within their domain.
+If available, they connect to the remote counterpart and locally create a proxy server connector (forwarding all communication).
+
+.Interaction diagram: Service Gateway - find service
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_service_gateway_find_service.puml[]
+....
+
+== Service Gateway - require service
+
+The creation of client connectors is forwarded to bridges as request_service() calls.
+Service bridges look up the required service instance within their domain.
+If available, they connect to the remote counterpart and locally create a proxy server connector (forwarding all communication).
+
+.Interaction diagram: Service Gateway - require service
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_service_gateway_require_service.puml[]
+....
+
+== Service Gateway - provide service
+
+The creation of server connectors is found by service bridges using the SOCom subscribe_find_service() API.
+Service bridges provide this information within their domain.
+If any domain partner requests the service instance, the service bridge creates a proxy client connector (forwarding all communication).
+
+.Interaction diagram: Service Gateway - provide service
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_service_gateway_provide_service.puml[]
+....
+
+== Deadlock detection
+
+In order to facilitate deadlock detection, before a callback is called by a Client_connector or
+Server_connector the thread id of the caller is saved.
+After the callback returns, the previously saved thread id is removed.
+If the calling object is destructed prematurely the deadlock is detected by checking if the thread
+id is still present, issuing a warning log and terminating the application.
+
+== Client_connector deadlocks
+
+When a running callback destroys the calling Client_connector a deadlock will happen, which will cause the application to be terminated.
+
+The deadlock is caused because the Client_connector destructor waits for the callback to return.
+The callback on the other hand waits for the destructor to return.
+
+.Interaction diagram: Client_connector deadlocks
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_client_connector_deadlocks.puml[]
+....
+
+== Server_connector deadlocks
+
+When a running callback destroys the calling Server_connector a deadlock will happen, which will cause the application to be terminated.
+
+The deadlock is caused because the Server_connector destructor waits for the callback to return.
+The callback on the other hand waits for the destructor to return.
+
+.Interaction diagram: Server_connector deadlocks
+[plantuml, svg, align="center"]
+....
+include::models/interaction_diagram_server_connector_deadlocks.puml[]
+....
diff --git a/src/socom/doc/design/50_state_dynamics_view.adoc b/src/socom/doc/design/50_state_dynamics_view.adoc
new file mode 100644
index 00000000..e628edb6
--- /dev/null
+++ b/src/socom/doc/design/50_state_dynamics_view.adoc
@@ -0,0 +1,115 @@
+// *******************************************************************************
+// Copyright (c) 2025 Contributors to the Eclipse Foundation
+//
+// See the NOTICE file(s) distributed with this work for additional
+// information regarding copyright ownership.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Apache License Version 2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: Apache-2.0
+// *******************************************************************************
+
+////
+Every asciidoc file in this document starts with the section level 0, i.e. a section like
+....
+= Title
+....
+
+When the file is included from another file within the document, the levels must be adjusted with the `leveloffset` attribute to the `include` directive at the point of inclusion, e.g.
+....
+include::file.adoc[leveloffset=+1]
+....
+////
+
+= State Dynamics View
+
+[#event_subscription_state_machine]
+== Event subscription state
+
+The following model describes the state-behavior of an event subscription from a logical perspective.
+
+.State diagram of event subscription state
+[plantuml, svg, align="center"]
+....
+include::models/state_diagram_event_subscription_state.puml[]
+....
+
+.States
+|====
+| Name | Description
+
+| Not_available
+| The client and server are not connected.
+No event subscription or transmission is possible.
+
+| Available
+| The client and server are connected and the event is not subscribed.
+No event update will be received in this state.
+
+| Event_subscribed
+| The user is subscribed to the event.
+Event updates will be received in this state.
+However, the server has not acknowledged the subscription yet.
+Thus clients do not know if the server will send event updates.
+|====
+
+.Triggers
+|====
+| Name | Description
+
+| Client_connector::subscribe_event()
+| The user calls Client_connector::subscribe_event() in order to subscribe to an event.
+
+| Client_connector::unsubscribe_event()
+| The user calls Client_connector::unsubscribe_event() in order to unsubscribe from an event.
+
+| Client_connector::Callbacks::on_service_state_change(Service_state::available)
+| SOCom calls the user callback Client_connector::Callbacks::on_service_state_change(Service_state::available).
+
+| Client_connector::Callbacks::on_service_state_change(!Service_state::available)
+| SOCom calls the user callback Client_connector::Callbacks::on_service_state_change() with a state different from Service_state::available.
+|====
+
+[#service_state_machine]
+== Service state
+
+The following model describes the state-behavior of a service instance as reported to an instance of type Client_connector by callback on_state_change().
+
+.State diagram of service state
+[plantuml, svg, align="center"]
+....
+include::models/state_diagram_service_state.puml[]
+....
+
+.States
+|====
+| Name | Description
+
+| Service_state::not_available
+| The service instance does not exist.
+No provided service instance within the service domain is known.
+
+| Service_state::available
+| The service instance exists, is connected and available.
+A provided service instance is known, identified and enabled.
+Thus the service instance can be used and service requests will be answered.
+|====
+
+.Triggers
+|====
+| Name | Description
+
+| Runtime::make_server_connector()
+| Construction of Disabled_server_connector by API function Runtime::make_server_connector().
+
+| Disabled_server_connector::enable()
+| Disabling of Enabled_server_connector by API function Enabled_server_connector::disable().
+
+| ~Disabled_server_connector()
+| Destruction of Disabled_server_connector().
+
+| ~Enabled_server_connector()
+| Destruction of Enabled_server_connector().
+|====
diff --git a/src/socom/doc/design/README.adoc b/src/socom/doc/design/README.adoc
new file mode 100644
index 00000000..f078292d
--- /dev/null
+++ b/src/socom/doc/design/README.adoc
@@ -0,0 +1,39 @@
+// *******************************************************************************
+// Copyright (c) 2025 Contributors to the Eclipse Foundation
+//
+// See the NOTICE file(s) distributed with this work for additional
+// information regarding copyright ownership.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Apache License Version 2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// SPDX-License-Identifier: Apache-2.0
+// *******************************************************************************
+
+= Software design SOCom library
+:description: SOCom library software element
+:docinfo: shared
+:diagramdir: models
+:imagesdir: images
+:toc: left
+:iconsdir: images
+:icons: font
+:sectnums:
+:sectnumlevels: 5
+
+// keep the structure of this document
+// 1st line = Document main title
+// 2nd line = Document sub title (this is what you see in web directory)
+// don't touch all the other attributes, they are needed by asciidoc
+// store external PUML files always to a models sub directory
+// store images always to a images sub directory
+// in this file shall only be include what is part of HTML documentation only
+
+
+[#software_element_socom]
+== SOCom library
+
+include::30_structural_view.adoc[leveloffset=+1]
+include::40_behavioral_view.adoc[leveloffset=+1]
+include::50_state_dynamics_view.adoc[leveloffset=+1]
diff --git a/src/socom/doc/design/models/component_diagram_socom.puml b/src/socom/doc/design/models/component_diagram_socom.puml
new file mode 100644
index 00000000..76ad016f
--- /dev/null
+++ b/src/socom/doc/design/models/component_diagram_socom.puml
@@ -0,0 +1,36 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+left to right direction
+package SOCom {
+ Component Client_connector
+ Component Server_connector
+ Component Runtime
+ Component Service_interface_definition
+}
+
+() "Client_connector" as Client_connector_if
+() "Disabled_server_connector" as Disabled_server_connector_if
+() "Enabled_server_connector" as Enabled_server_connector_if
+() "Runtime" as Runtime_if
+
+Client_connector_if -- Client_connector
+Disabled_server_connector_if -- Server_connector
+Enabled_server_connector_if -- Server_connector
+Runtime_if -- Runtime
+
+Client_connector --> Service_interface_definition : depends
+Server_connector --> Service_interface_definition : depends
+Runtime --> Service_interface_definition : depends
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_client_connector_deadlocks.puml b/src/socom/doc/design/models/interaction_diagram_client_connector_deadlocks.puml
new file mode 100644
index 00000000..37ff70f2
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_client_connector_deadlocks.puml
@@ -0,0 +1,122 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant Client_callbacks
+participant Client_connector
+participant Server_connector
+
+== Deadlock by on_service_state_change ==
+
+Server_connector --> Client_connector: state_changed()
+activate Client_connector
+
+Client_connector --> Client_callbacks: on_service_state_change()
+activate Client_callbacks
+
+Client_callbacks --> Client_connector: ~Client_connector()
+destroy Client_connector
+deactivate Client_callbacks
+
+rnote over Client_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+
+== Deadlock by on_event_update ==
+
+Server_connector --> Client_connector: update_event()
+activate Client_connector
+
+Client_connector --> Client_callbacks: on_event_update()
+activate Client_callbacks
+
+Client_callbacks --> Client_connector: ~Client_connector()
+destroy Client_connector
+deactivate Client_callbacks
+
+rnote over Client_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+
+== Deadlock by on_event_requested_update ==
+
+Server_connector --> Client_connector: update_requested_event()
+activate Client_connector
+
+Client_connector --> Client_callbacks: on_event_requested_update()
+activate Client_callbacks
+
+Client_callbacks --> Client_connector: ~Client_connector()
+destroy Client_connector
+deactivate Client_callbacks
+
+rnote over Client_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+
+== Deadlock by on_event_subscription_status_change ==
+
+Server_connector --> Client_connector: set_event_subscription_state()
+activate Client_connector
+
+Client_connector --> Client_callbacks: on_event_subscription_status_change ()
+activate Client_callbacks
+
+Client_callbacks --> Client_connector: ~Client_connector()
+destroy Client_connector
+deactivate Client_callbacks
+
+rnote over Client_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+
+== Deadlock by on_method_reply at client ==
+
+Client_connector --> Server_connector: call_method()
+activate Client_connector
+activate Server_connector
+
+Server_connector --> Client_connector: method_reply()
+
+Client_connector --> Client_callbacks: on_method_reply()
+activate Client_callbacks
+
+Client_callbacks --> Client_connector: ~Client_connector()
+destroy Client_connector
+deactivate Client_callbacks
+deactivate Server_connector
+
+rnote over Client_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+
+== Deadlock by on_method_reply at server ==
+
+Client_connector --> Server_connector: call_method()
+activate Client_connector
+activate Server_connector
+
+Server_connector --> Client_connector: method_reply()
+
+Client_connector --> Client_callbacks: on_method_reply()
+activate Client_callbacks
+
+Client_callbacks --> Server_connector: ~Server_connector()
+deactivate Client_connector
+deactivate Client_callbacks
+destroy Server_connector
+
+rnote over Client_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_event_communication.puml b/src/socom/doc/design/models/interaction_diagram_event_communication.puml
new file mode 100644
index 00000000..dda5e47c
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_event_communication.puml
@@ -0,0 +1,63 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant Client_1 <>
+participant SOCom
+participant Server <>
+autonumber
+activate Server
+
+activate Client_1
+
+Client_1 -> SOCom : subscribe_event(x, update)
+activate SOCom
+
+SOCom -> Server : on_event_subscription_change(x, subscribed)
+activate Server
+
+return
+
+return
+
+== ... ==
+
+Server -> Server : updated event
+
+Server -> SOCom : allocate_event_payload(x)
+activate SOCom
+SOCom -> Client_1 : on_event_payload_allocate(x)
+activate Client_1
+return writable_payload
+return writable_payload
+
+Server -> SOCom : update_event(x, payload)
+activate SOCom
+
+SOCom -> Client_1 : on_event_update(x, payload)
+activate Client_1
+return
+
+return
+
+== ... ==
+
+Client_1 -> SOCom : unsubscribe_event(x)
+activate SOCom
+
+SOCom -> Server : on_event_subscription_change(x, unsubscribed)
+activate Server
+return
+
+return
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_field_notification_communication.puml b/src/socom/doc/design/models/interaction_diagram_field_notification_communication.puml
new file mode 100644
index 00000000..efde36d3
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_field_notification_communication.puml
@@ -0,0 +1,71 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant Client_1 <>
+participant SOCom
+participant Server <>
+
+autonumber
+
+activate Server
+activate Client_1
+
+Server -> Server : field_update(x, payload)
+
+Client_1 -> SOCom : subscribe_event(x, update_and_initial_value)
+
+activate SOCom
+
+SOCom -> Server : on_event_subscription_change(x, subscribed)
+activate Server
+return
+
+SOCom -> Server : request_event_update(x)
+activate Server
+return
+
+return
+
+Server -> SOCom : update_requested_event(x, payload)
+activate SOCom
+SOCom -> Client_1 : on_requested_event_update(x, payload)
+activate Client_1
+return
+return
+
+== ... ==
+
+Server -> Server : field_update(x, payload)
+
+Server -> SOCom : update_event(x, payload)
+activate SOCom
+
+SOCom -> Client_1 : on_event_update(x, payload)
+activate Client_1
+return
+
+return
+
+== ... ==
+
+Client_1 -> SOCom : unsubscribe_event(x)
+activate SOCom
+
+SOCom -> Server : on_event_subscription_change(x, unsubscribed)
+activate Server
+return
+
+return
+deactivate Client_1
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_method_communication.puml b/src/socom/doc/design/models/interaction_diagram_method_communication.puml
new file mode 100644
index 00000000..ef8b7d4a
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_method_communication.puml
@@ -0,0 +1,70 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant Client_1 <>
+participant SOCom
+participant Server <>
+autonumber
+activate Server
+activate Client_1
+
+== Reply requested ==
+
+Client_1 -> SOCom : allocate_method_call_payload(x)
+activate SOCom
+SOCom -> Server : on_method_call_payload_allocate(x)
+activate Server
+return writable_payload
+return writable_payload
+
+Client_1 -> SOCom : call_method(x, call_payload, reply_data)
+
+activate SOCom
+SOCom -> Server : on_method_call(x, call_payload, reply_data)
+activate Server
+Server -> Server : process method x
+return
+return
+
+note right of Server
+ Server might write data into reply_data.reply_payload
+end note
+
+Server -> SOCom : reply_data.reply_callback(reply_data.reply_payload)
+activate SOCom
+SOCom -> Client_1 : on_method_reply(x, reply_payload)
+activate Client_1
+return
+SOCom --> Server
+
+deactivate SOCom
+== Fire and forget (no reply) ==
+
+Client_1 -> SOCom : allocate_method_call_payload(x)
+activate SOCom
+SOCom -> Server : on_method_call_payload_allocate(x)
+activate Server
+return writable_payload
+return writable_payload
+
+Client_1 -> SOCom : call_method(x, call_payload, nullopt)
+activate SOCom
+
+SOCom -> Server : on_method_call(x, call_payload, nullopt)
+activate Server
+
+Server -> Server : process method x
+return
+return
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_server_connector_deadlocks.puml b/src/socom/doc/design/models/interaction_diagram_server_connector_deadlocks.puml
new file mode 100644
index 00000000..1e7d928d
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_server_connector_deadlocks.puml
@@ -0,0 +1,70 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant Server_callbacks
+participant Server_connector
+participant Client_connector
+
+' Method_call_callback on_method_call;
+' Event_subscription_change_callback on_event_subscription_change;
+' Event_request_update_callback on_event_update_request;
+
+== Deadlock by on_method_call ==
+
+Client_connector --> Server_connector: call_method()
+activate Server_connector
+
+Server_connector --> Server_callbacks: on_method_call()
+activate Server_callbacks
+
+Server_callbacks --> Server_connector: ~Server_connector()
+destroy Server_connector
+deactivate Server_callbacks
+
+rnote over Server_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+
+== Deadlock by on_event_subscription_change ==
+
+Client_connector --> Server_connector: subscribe_event()
+activate Server_connector
+
+Server_connector --> Server_callbacks: on_event_subscription_change()
+activate Server_callbacks
+
+Server_callbacks --> Server_connector: ~Server_connector()
+destroy Server_connector
+deactivate Server_callbacks
+
+rnote over Server_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+
+== Deadlock by on_event_update_request ==
+
+Client_connector --> Server_connector: request_event_update()
+activate Server_connector
+
+Server_connector --> Server_callbacks: on_event_update_request()
+activate Server_callbacks
+
+Server_callbacks --> Server_connector: ~Server_connector()
+destroy Server_connector
+deactivate Server_callbacks
+
+rnote over Server_connector
+ Deadlock happens here because destructor waits for callback to finish.
+endrnote
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_service_gateway_find_service.puml b/src/socom/doc/design/models/interaction_diagram_service_gateway_find_service.puml
new file mode 100644
index 00000000..076560d7
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_service_gateway_find_service.puml
@@ -0,0 +1,88 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant app as "Application" <>
+participant socom as "SOCom"
+participant bridge as "Generic service bridge" <>
+autonumber
+
+== Initialization ==
+
+activate app
+app -> socom ** : create_runtime()
+
+app -> bridge ** : create_bridge_component()
+activate bridge
+
+bridge -> socom : register_service_bridge\n(bridge_subscribe_find_service, bridge_request_service)
+activate socom
+return
+
+bridge -> socom : subscribe_find_service\n(bridge_find_callback)
+activate socom
+return
+
+note right
+A generic service bridge (e.g. bridge::ipc)
+is interested in every service instance
+within the local process.
+This enables a config-free (dynamic)
+implementation of service discovery functionality
+based on the pure internal service instance identifiers.
+end note
+
+
+== Application searches for a service instance ==
+
+app -> socom : subscribe_find_service\n(app_find_callback, interface_A, instance_1)
+activate socom
+socom -> bridge : bridge_subscribe_find_service\n(socom_find_callback, interface_A, instance_1)
+activate bridge
+
+group Implementation detail
+bridge -> bridge
+note right
+The generic service bridge
+searches for the service instance on
+its remote counterparts.
+end note
+end
+return
+
+return
+
+
+== service instance remote search result changed (added)==
+
+group Implementation detail
+bridge -> bridge
+note right
+Found a service instance on a remote counterpart.
+end note
+end
+
+bridge -> socom : socom_find_callback\n(interface_A, instance_1, State::added)
+activate socom
+socom -> app : app_find_callback\n(interface_A, instance_1, State::added)
+activate app
+return
+
+return
+
+note right
+Important: SOCom does not forward find results to
+service-interface wildcard finds
+(subscribe_find_service(callback)).
+end note
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_service_gateway_provide_service.puml b/src/socom/doc/design/models/interaction_diagram_service_gateway_provide_service.puml
new file mode 100644
index 00000000..3c3728a8
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_service_gateway_provide_service.puml
@@ -0,0 +1,95 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant app as "Application" <>
+participant socom as "SOCom"
+participant bridge as "Generic service bridge" <>
+autonumber
+
+== Initialization ==
+
+activate app
+app -> socom ** : create_runtime()
+activate socom
+app -> bridge ** : create_bridge_component()
+activate bridge
+
+bridge -> socom : register_service_bridge(bridge_subscribe_find_service, bridge_request_service)
+activate socom
+return
+
+bridge -> socom : subscribe_find_service(bridge_find_callback)
+activate socom
+return
+
+note right
+A generic service bridge (e.g. bridge::ipc)
+is interested in every service instance
+within the local process.
+This enables a config-free (dynamic)
+implementation of service discovery functionality
+based on the pure internal service instance identifiers.
+end note
+
+
+== Application creates a service instance ==
+
+app -> socom : make_server_connector(interface_A, instance_1)
+activate socom
+socom -> bridge : bridge_find_callback(interface_A, instance_1, State::added)
+activate bridge
+
+group Implementation detail
+bridge -> bridge
+note right
+The generic service bridge
+informs its remote counterparts
+about the availability
+of a new service instance
+interface_A, instance_1
+at the local endpoint.
+end note
+end
+return
+
+return
+
+== service instance remote usage ==
+
+group Implementation detail
+bridge -> bridge
+note right
+A remote counterpart is interested
+in the local service and informed the
+generic service bridge about.
+end note
+end
+
+bridge -> socom : make_client_connector(interface_A, instance_1)
+activate socom
+return
+
+group !!! Important: break circle !!!
+socom X--> bridge : bridge_request_service(interface_A, instance_1)
+note right #FFAAAA
+SOCom shall call bridge_request_service()
+only if service instance is locally not available.
+end note
+end
+
+socom -> socom
+note right
+SOCom connects client_connector to server_connector.
+end note
+@enduml
diff --git a/src/socom/doc/design/models/interaction_diagram_service_gateway_require_service.puml b/src/socom/doc/design/models/interaction_diagram_service_gateway_require_service.puml
new file mode 100644
index 00000000..f5a1e7d2
--- /dev/null
+++ b/src/socom/doc/design/models/interaction_diagram_service_gateway_require_service.puml
@@ -0,0 +1,93 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+participant app as "Application" <>
+participant socom as "SOCom"
+participant bridge as "Generic service bridge" <>
+autonumber
+
+== Initialization ==
+
+activate app
+app -> socom ** : create_runtime()
+activate socom
+
+app -> bridge ** : create_bridge_component()
+activate bridge
+
+bridge -> socom : register_service_bridge\n(bridge_subscribe_find_service, bridge_request_service)
+activate socom
+return
+
+bridge -> socom : subscribe_find_service(bridge_find_callback)
+activate socom
+return
+
+note right
+A generic service bridge (e.g. bridge::ipc)
+is interested in every service instance
+within the local process.
+This enables a config-free (dynamic)
+implementation of service discovery functionality
+based on the pure internal service instance identifiers.
+end note
+
+
+== Application requests a service instance ==
+
+app -> socom : make_client_connector(interface_A, instance_1)
+activate socom
+socom -> bridge : bridge_request_service(interface_A, instance_1)
+activate bridge
+
+group Implementation detail
+bridge -> bridge
+note right
+The generic service bridge
+requests for the service instance on
+its remote counterparts.
+end note
+end
+return
+
+app <-- socom
+deactivate socom
+
+== remote service instance connect ==
+
+group Implementation detail
+bridge -> bridge
+note right
+The generic service bridge connects to a remote counterpart
+which hosts the service instance.
+end note
+end
+
+bridge -> socom : make_server_connector(interface_A, instance_1)
+activate socom
+return
+
+group !!! Important: break circle !!!
+socom -X bridge : bridge_find_callback(interface_A, instance_1, State::added)
+note right #FFAAAA
+The bridge shall detect that find result is
+caused by it's own created server_connector.
+end note
+end
+
+socom -> socom
+note right
+SOCom connects client_connector to server_connector.
+end note
+@enduml
diff --git a/src/socom/doc/design/models/state_diagram_event_subscription_state.puml b/src/socom/doc/design/models/state_diagram_event_subscription_state.puml
new file mode 100644
index 00000000..dd158287
--- /dev/null
+++ b/src/socom/doc/design/models/state_diagram_event_subscription_state.puml
@@ -0,0 +1,30 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+hide empty description
+
+state Not_available
+state Available
+state Event_subscribed
+
+[*] --> Not_available
+Not_available --> Available: Client_connector::Callbacks::\non_service_state_change\n(Service_state::available)
+
+Available --> Not_available: Client_connector::Callbacks::\non_service_state_change\n(!Service_state::available)
+Available --> Event_subscribed: Client_connector::\nsubscribe_event()
+
+Event_subscribed --> Available: Client_connector::\nunsubscribe_event()
+Event_subscribed --> Not_available: Client_connector::Callbacks::\non_service_state_change\n(!Service_state::available)
+
+@enduml
diff --git a/src/socom/doc/design/models/state_diagram_service_state.puml b/src/socom/doc/design/models/state_diagram_service_state.puml
new file mode 100644
index 00000000..8a56320c
--- /dev/null
+++ b/src/socom/doc/design/models/state_diagram_service_state.puml
@@ -0,0 +1,29 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml
+hide empty description
+
+state "Service_state::not_available" as not_available
+state "Service_state::available" as available
+
+[*] --> not_available
+
+not_available --> not_available: Runtime::make_server_connector()
+not_available --> available: Disabled_server_connector::enable()
+
+available --> not_available: Enabled_server_connector::disable()
+available --> not_available: \~Enabled_server_connector()
+
+not_available --> not_available: \~Disabled_server_connector():
+@enduml
diff --git a/src/socom/doc/design/models/zero_copy_event_update.puml b/src/socom/doc/design/models/zero_copy_event_update.puml
new file mode 100644
index 00000000..255d4e6e
--- /dev/null
+++ b/src/socom/doc/design/models/zero_copy_event_update.puml
@@ -0,0 +1,109 @@
+' *******************************************************************************
+' Copyright (c) 2025 Contributors to the Eclipse Foundation
+'
+' See the NOTICE file(s) distributed with this work for additional
+' information regarding copyright ownership.
+'
+' This program and the accompanying materials are made available under the
+' terms of the Apache License Version 2.0 which is available at
+' https://www.apache.org/licenses/LICENSE-2.0
+'
+' SPDX-License-Identifier: Apache-2.0
+' *******************************************************************************
+
+@startuml Zero Copy Event Update from Network
+
+actor network as "Network"
+
+box Network Daemon
+
+participant someip_network_impl as "SOME/IP Network implementation"
+participant socom_server_nd as "socom::Server_connector"
+participant socom_client_nd as "socom::Client_connector"
+
+end box
+
+participant ipc as "Message passing IPC"
+
+box Gateway Daemon
+
+participant socom_server_gd as "socom::Server_connector"
+participant socom_client_gd as "socom::Client_connector"
+participant gateway as "Gatway logic"
+participant mwcom as "mw::com"
+
+end box
+
+note right network
+socom was design with a 1:n relationship. An event update by a Server_connector
+is distributed to multiple Client_connectors. However this does not work well
+with zero-copy.
+
+For zero-copy there needs to be a 1:1 relationship between Server_connector
+and Client_connector. The assumption is that Message passing / mw::com
+will do the multiplexing if needed.
+end note
+
+== Read event from network into IPC buffer ==
+
+network -> someip_network_impl : Receive event update notification
+
+activate someip_network_impl
+someip_network_impl -> socom_server_nd : allocate_event_update()
+activate socom_server_nd
+socom_server_nd -> socom_client_nd : allocate_payload()
+
+note right
+Call is actually callback through Client_connector and does not really invoke Client_connector code.
+Actually the Server_connector calls a callback which directly uses Message Passing to allocate the buffer.
+
+This applies to all Client_connector calls in this diagram.
+end note
+
+activate socom_client_nd
+socom_client_nd -> ipc : allocate_ipc_buffer()
+activate ipc
+return Buffer
+return Buffer
+return Buffer
+someip_network_impl -> network : Read event update into buffer
+
+== Send event update via IPC to Gateway Daemon ==
+
+someip_network_impl -> socom_server_nd : update_event(event_id, Buffer)
+activate socom_server_nd
+socom_server_nd -> socom_client_nd : update_event(event_id, Buffer)
+activate socom_client_nd
+socom_client_nd -> ipc : send_event_update(event_id, Buffer)
+activate ipc
+return
+return
+return
+deactivate someip_network_impl
+
+== Gateway Daemon allocates buffer for event update ==
+
+ipc -> socom_server_gd : receive_event_update(event_id, Buffer)
+activate socom_server_gd
+socom_server_gd -> socom_client_gd : allocate_event_payload(event_id)
+activate socom_client_gd
+socom_client_gd -> gateway : allocate()
+activate gateway
+gateway -> mwcom : allocate(event_id)
+activate mwcom
+return Buffer
+return Buffer
+return Buffer
+
+== Gateway Daemon processes event update ==
+
+socom_server_gd -> socom_client_gd : update_event(event_id, Buffer)
+activate socom_client_gd
+socom_client_gd -> gateway : update_event(event_id, Buffer)
+activate gateway
+gateway -> gateway : E2E check
+gateway -> gateway : Process event update
+
+gateway -> mwcom : send(event_id, processed Buffer)
+
+@enduml
diff --git a/src/socom/include/score/socom/client_connector.hpp b/src/socom/include/score/socom/client_connector.hpp
new file mode 100644
index 00000000..e8e4abf3
--- /dev/null
+++ b/src/socom/include/score/socom/client_connector.hpp
@@ -0,0 +1,249 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SRC_SOCOM_INCLUDE_SCORE_SOCOM_CLIENT_CONNECTOR
+#define SRC_SOCOM_INCLUDE_SCORE_SOCOM_CLIENT_CONNECTOR
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+
+class Client_connector;
+
+/// \brief Service states from the service user viewpoint.
+enum class Service_state : std::uint8_t {
+ /// Service is not available.
+ not_available = 0,
+ /// Service is available.
+ available = 1,
+};
+
+/// \brief Function type for indicating service state changes to the service user.
+using Service_state_change_callback = score::cpp::move_only_function;
+
+/// \brief Function type for indicating event updates to the service user.
+using Event_update_callback =
+ score::cpp::move_only_function;
+
+/// \brief Function type for allocating event payloads.
+using Event_payload_allocate_callback =
+ score::cpp::move_only_function>(
+ Client_connector const&, Event_id)>;
+
+/// \brief Interface for applications to use a service (client-role).
+/// \details Changes of service instance state are indicated by callback on_service_state_change.
+///
+/// A Client_connector instance is connected to a Server_connector instance only if the service
+/// interfaces are compatible. The compatibility check contains checks for semantic version and
+/// service interface members.
+///
+/// If the service state is not Service_state::available, service API calls have no effect and
+/// return Error::runtime_error_service_not_available.
+///
+/// If the passed parameter client_id is not valid (not contained in the client connector or server
+/// connector specific Service_interface_definition), service API calls have no effect and
+/// return Error::logic_error_id_out_of_range.
+///
+/// If the Service_interface_definition of the Client_connector instance contains a member
+/// configuration, then the client_id is translated to the server_id based on the matching member
+/// names (configuration.members.events and configuration.members.methods).
+///
+/// If the Service_interface_definition of the Client_connector instance does not contain a
+/// member configuration, then the client_id is translated to the server_id 1:1.
+///
+/// The Client_connector callback on_event_update is called if an available Enabled_server_connector
+/// calls update_event().
+///
+/// The Client_connector callback on_event_requested_update is called if an available
+/// Enabled_server_connector instance calls update_requested_event() and the
+/// Client_connector instance previously requested an event update with request_event_update().
+class Client_connector {
+ public:
+ /// \brief Alias for an unique pointer to this interface.
+ using Uptr = std::unique_ptr;
+
+ /// \brief Client_connector callback interface needed at Client_connector construction, see
+ /// Runtime::make_client_connector().
+ ///
+ /// \attention All user callbacks must not block and shall return quickly (simple algorithms
+ /// only). No callback is allowed to destroy the Client_connector, otherwise it will result in a
+ /// deadlock. If a deadlock situation is detected, a warning will be logged and the application
+ /// terminated.
+ struct Callbacks {
+ /// \brief Callback is called on any service state change.
+ Service_state_change_callback on_service_state_change;
+ /// \brief Callback is called on a server triggered event update.
+ Event_update_callback on_event_update;
+ /// \brief Callback is called on a client requested event update, see
+ /// Client_connector::subscribe_event() and Client_connector::request_event_update().
+ Event_update_callback on_event_requested_update;
+ /// \brief Callback is called to allocate event payloads.
+ Event_payload_allocate_callback on_event_payload_allocate;
+ };
+
+ /// \brief Constructor.
+ /// \details A Client_connector instance and a Server_connector instance do not match under the
+ /// following conditions:
+ /// - the instance.id of both is different;
+ /// - the configuration.interface.id of both is different;
+ /// - the configuration.interface.version.major of both is different;
+ /// - the Client_connector's configuration.interface.version.minor is larger than the
+ /// Server_connector's.
+ ///
+ /// After construction the service state is always Service_state::not_available.
+ /// This initial state is not indicated through the callback on_service_state_change().
+ ///
+ /// If the SOCom service registry connects a Client_connector to the matching Server_connector,
+ /// then the SOCom service registry calls the callback
+ /// on_service_state_change(Service_state::available, ...).
+ ///
+ /// If the SOCom service registry disconnects an available Server_connector from a
+ /// Client_connector, then the Client_connector calls the callback
+ /// on_service_state_change(Service_state::not_available, ...).
+ Client_connector() = default;
+
+ /// \brief Destructor.
+ /// \details Unregisters from the server connector and destroys the client connector.
+ /// After destruction no registered callbacks are called any more.
+ ///
+ /// Blocks until all operations have completed and the service state is (implicitly)
+ /// Service_state::not_available. It does not call callback on_service_state_change().
+ ///
+ /// Aborts all method calls. This ensures no method reply will be invoked after completion the
+ /// destructor.
+ ///
+ /// Implicitly unsubscribes all subscribed events.
+ ///
+ /// Detect deadlocks, which are caused by destroying the Client_connector from a running
+ /// Client_connector callback. When a deadlock is detected, the destructor logs and
+ /// terminates the application.
+ virtual ~Client_connector() noexcept = default;
+
+ Client_connector(Client_connector const&) = delete;
+ Client_connector(Client_connector&&) = delete;
+
+ Client_connector& operator=(Client_connector const&) = delete;
+ Client_connector& operator=(Client_connector&&) = delete;
+
+ /// \brief Allocate a payload for the given method ID.
+ ///
+ /// This requires a Server_connector to be connected to which payload allocation is delegated.
+ ///
+ /// \param method_id ID of the method for which a payload should be allocated.
+ /// \return A writable payload in case of successful operation, otherwise an error.
+ [[nodiscard]]
+ virtual Result> allocate_method_call_payload(
+ Method_id method_id) noexcept = 0;
+
+ /// \brief Subscribe an event to receive event updates from the Server_connector.
+ /// \details The mode value Event_mode::update_and_initial_value supports the field use-case.
+ ///
+ /// The user is responsible for calling subscribe_event() again, if the service state
+ /// transitions to Service_state::available and a subscription is required.
+ ///
+ /// If the service state is Service_state::available, then the Enabled_server_connector instance
+ /// registers this Client_connector as subscribed for event server_id.
+ ///
+ /// The available Enabled_server_connector instance combines the mode parameter with modes of
+ /// other clients subscription's and stores the result.
+ ///
+ /// The mode value Event_mode::update_and_initial_value is dominant while mode value
+ /// Event_mode::update is recessive.
+ ///
+ /// If one subscription requests mode value Event_mode::update_and_initial_value,
+ /// then the resulting stored mode value is Event_mode::update_and_initial_value.
+ ///
+ /// All subscriptions are lost if the service state Service_state::available is left.
+ ///
+ /// If this is the first subscription for this event server_id at the Enabled_server_connector
+ /// instance (no matter from which Client_connector), then the Enabled_server_connector instance
+ /// calls callback on_event_subscription_change(server_id, Event_state::subscribed).
+ ///
+ /// If this is the first subscription for this event server_id at the Enabled_server_connector
+ /// instance and the parameter mode is Event_mode::update_and_initial_value, then the
+ /// Enabled_server_connector instance stores the Client_connector instance in a list of update
+ /// requesters for event server_id and calls callback on_event_update_request(server_id) after
+ /// calling callback on_event_subscription_change().
+ ///
+ /// \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 subscribe_event(Event_id client_id, Event_mode mode) const noexcept = 0;
+
+ /// \brief Unsubscribes from an event to stop receiving event updates.
+ /// \details If the service state is Service_state::available, then the available
+ /// Enabled_server_connector instance unregisters this Client_connector for event server_id and
+ /// removes this Client_connector instance from the list of update requesters for event
+ /// server_id.
+ ///
+ /// If this is the last Client_connector instance unsubscribing for a specific event server_id
+ /// at the Enabled_server_connector instance, then the Enabled_server_connector instance calls
+ /// callback on_event_subscription_change(server_id, Event_state::not_subscribed).
+ /// \param client_id ID of the event.
+ /// \return Void in case of successful operation, otherwise an error.
+ virtual Result unsubscribe_event(Event_id client_id) const noexcept = 0;
+
+ /// \brief Requests an event update.
+ /// \details If the service state is Service_state::available, then the available
+ /// Enabled_server_connector instance stores the Client_connector instance in a list of update
+ /// requesters for event server_id and calls callback on_event_update_request(server_id) if this
+ /// is the first update_request for the event.
+ /// \param client_id ID of the event.
+ /// \return Void in case of successful operation, otherwise an error.
+ virtual Result request_event_update(Event_id client_id) const noexcept = 0;
+
+ /// \brief Calls a method at the Server_connector side.
+ /// \details If reply_data is nullopt, then the Server application (of the
+ /// Enabled_server_connector instance) and the Method_invocation object returned do not allocate
+ /// any resources for this method call and callback on_method_reply() will not be called.
+ ///
+ /// If reply_data is not nullopt, then the server application (of the
+ /// Enabled_server_connector instance) returns a Method_invocation object which allocates
+ /// resources required for the ongoing method invocation. Once the method invocation is
+ /// completed, the server application calls reply_data.reply_callback().
+ /// Discarding the Method_invocation object cancels the method invocation.
+ ///
+ /// If the service state is Service_state::available, then the available Server_connector
+ /// instance calls the callback on_method_call(server_id, payload, reply_data).
+ /// \param client_id ID of the method.
+ /// \param payload Payload to be called with.
+ /// \param reply_data Callback and payload buffer in case a reply is requested.
+ /// \return A pointer to a Method_invocation object in case of successful invocation, otherwise
+ /// an error.
+ [[nodiscard]] virtual Result call_method(
+ Method_id client_id, Payload::Sptr payload,
+ Method_call_reply_data_opt reply_data = std::nullopt) const noexcept = 0;
+
+ /// \brief Retrieves the peer posix credentials from the server.
+ /// \details If the client connector is not connected, then an error is returned.
+ /// \return Posix credentials in case of successful operation, otherwise an error.
+ [[nodiscard]] virtual Result get_peer_credentials() const noexcept = 0;
+
+ [[nodiscard]] virtual Service_interface_definition const& get_configuration()
+ const noexcept = 0;
+ [[nodiscard]] virtual Service_instance const& get_service_instance() const noexcept = 0;
+ [[nodiscard]] virtual bool is_service_available() const noexcept = 0;
+};
+
+} // namespace score::socom
+
+#endif // SRC_SOCOM_INCLUDE_SCORE_SOCOM_CLIENT_CONNECTOR
diff --git a/src/socom/include/score/socom/error.hpp b/src/socom/include/score/socom/error.hpp
new file mode 100644
index 00000000..a221f8d1
--- /dev/null
+++ b/src/socom/include/score/socom/error.hpp
@@ -0,0 +1,64 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_ERROR_HPP
+#define SCORE_SOCOM_ERROR_HPP
+
+#include
+
+#include "score/result/error_code.h"
+#include "score/result/error_domain.h"
+#include "score/result/result.h"
+
+namespace score::socom {
+
+/// \brief Error conditions when using Client_connector.
+enum class Error : score::result::ErrorCode {
+ /// Service state is not Service_state::available. Service_state::available cannot prevent
+ /// network issues, so if it is important that the Server receives a method call, it always has
+ /// to send some return value via the callback.
+ runtime_error_service_not_available,
+ /// Request is rejected.
+ runtime_error_request_rejected,
+ /// Event or method ID is out of range.
+ logic_error_id_out_of_range,
+ /// Payload cannot be deserialized.
+ runtime_error_malformed_payload,
+ /// Access is denied.
+ runtime_error_permission_not_allowed,
+};
+
+score::result::Error MakeError(Error code, std::string_view user_message = "") noexcept;
+
+/// \brief Error conditions when using Enabled_server_connector.
+enum class Server_connector_error : score::result::ErrorCode {
+ /// Event or method ID is out of range.
+ logic_error_id_out_of_range,
+ runtime_error_no_client_subscribed_for_event,
+};
+
+score::result::Error MakeError(Server_connector_error code,
+ std::string_view user_message = "") noexcept;
+
+/// \brief Errors upon connector construction.
+enum class Construction_error : score::result::ErrorCode {
+ duplicate_service, ///< Service identifier already exists.
+ callback_missing ///< At least one of the provided callbacks is missing.
+};
+
+score::result::Error MakeError(Construction_error code,
+ std::string_view user_message = "") noexcept;
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_ERROR_HPP
diff --git a/src/socom/include/score/socom/event.hpp b/src/socom/include/score/socom/event.hpp
new file mode 100644
index 00000000..df0b42e0
--- /dev/null
+++ b/src/socom/include/score/socom/event.hpp
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_EVENT_HPP
+#define SCORE_SOCOM_EVENT_HPP
+
+#include
+
+namespace score::socom {
+
+/// \brief Alias for an event ID.
+using Event_id = std::uint16_t;
+
+/// \brief Mode of an event.
+enum class Event_mode : std::uint8_t {
+ update = 0U, ///< Without initial value request.
+ update_and_initial_value ///< With initial value request.
+};
+
+/// \brief State of an event subscription.
+enum class Event_state : std::uint8_t {
+ /// Enabled_server_connector: There is no Client_connector subscribed to the event.
+ /// Client_connector: The Enabled_server_connector did not acknowledge or reject the
+ /// subscription.
+ unsubscribed,
+ /// Enabled_server_connector: There is at least one Client_connector subscribed to the event.
+ /// Client_connector: The Enabled_server_connected acknowledged the subscription.
+ subscribed
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_EVENT_HPP
diff --git a/src/socom/include/score/socom/method.hpp b/src/socom/include/score/socom/method.hpp
new file mode 100644
index 00000000..051f8083
--- /dev/null
+++ b/src/socom/include/score/socom/method.hpp
@@ -0,0 +1,143 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_METHOD_HPP
+#define SCORE_SOCOM_METHOD_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+/// \brief Alias for a method ID.
+using Method_id = std::uint16_t;
+
+/// \brief A variadic template struct that implements the Visitor pattern.
+/// \details The Visitor struct inherits from a parameter pack of types 'Ts' and aggregates their
+/// operator() overloads. This allows for seamless type-specific operations on multiple types
+/// without modifying their definitions.
+/// \tparam Ts Types to be combined into the Visitor.
+template
+struct Visitor : Ts... {
+ using Ts::operator()...;
+};
+
+/// \brief A template deduction guide that simplifies the syntax for creating instances of Visitor
+/// by eliminating the need to specify the template arguments.
+/// \tparam Ts Types to be deduced from the constructor parameters.
+template
+Visitor(Ts...) -> Visitor;
+
+/// \brief Interface class for method call RAII type (see Client_connector::call_method).
+class Method_invocation {
+ public:
+ /// \brief Alias for an unique pointer to this interface.
+ using Uptr = std::unique_ptr;
+
+ Method_invocation() = default;
+ virtual ~Method_invocation() = default;
+
+ Method_invocation(Method_invocation const&) = delete;
+ Method_invocation(Method_invocation&&) = delete;
+
+ Method_invocation& operator=(Method_invocation const&) = delete;
+ Method_invocation& operator=(Method_invocation&&) = delete;
+};
+
+/// \brief Result of successful method call.
+struct Application_return {
+ /// \brief Constructor.
+ /// \param p Payload data.
+ explicit Application_return(Payload::Sptr p = empty_payload()) : payload{std::move(p)} {}
+
+ /// \brief Payload data.
+ Payload::Sptr payload;
+};
+
+/// \brief Result of failed method call.
+struct Application_error {
+ /// \brief Alias for an error code.
+ using Code = std::int32_t;
+
+ /// \brief Constructor.
+ /// \param p Payload data.
+ explicit Application_error(Payload::Sptr p = empty_payload()) : payload{std::move(p)} {}
+
+ /// \brief Constructor.
+ /// \param c Error code.
+ /// \param p Payload data.
+ explicit Application_error(Code c, Payload::Sptr p = empty_payload())
+ : code{c}, payload{std::move(p)} {}
+
+ /// \brief Error code.
+ Code code{};
+
+ /// \brief Payload data.
+ Payload::Sptr payload;
+};
+
+/// \brief Alias for the response of a method.
+using Method_result = std::variant;
+
+/// \brief Operator == for Application_return.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of equality, otherwise false.
+inline bool operator==(Application_return const& lhs, Application_return const& rhs) {
+ return *lhs.payload == *rhs.payload;
+}
+
+/// \brief Operator != for Application_return.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of inequality, otherwise false.
+inline bool operator!=(Application_return const& lhs, Application_return const& rhs) {
+ return !(lhs == rhs);
+}
+
+/// \brief Operator == for Application_error.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of equality, otherwise false.
+inline bool operator==(Application_error const& lhs, Application_error const& rhs) {
+ return (lhs.code == rhs.code) && (*lhs.payload == *rhs.payload);
+}
+
+/// \brief Operator != for Application_error.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of inequality, otherwise false.
+inline bool operator!=(Application_error const& lhs, Application_error const& rhs) {
+ return !(lhs == rhs);
+}
+
+/// \brief Alias for the callback function of a method, in case a reply is requested.
+using Method_reply_callback = score::cpp::move_only_function;
+
+/// \brief Callback and payload buffer for method call replies.
+struct Method_call_reply_data {
+ Method_reply_callback reply_callback;
+ Writable_payload::Uptr reply_payload;
+
+ Method_call_reply_data(Method_reply_callback reply_callback,
+ Writable_payload::Uptr reply_payload);
+};
+
+using Method_call_reply_data_opt = std::optional;
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_METHOD_HPP
diff --git a/src/socom/include/score/socom/payload.hpp b/src/socom/include/score/socom/payload.hpp
new file mode 100644
index 00000000..fd821fd2
--- /dev/null
+++ b/src/socom/include/score/socom/payload.hpp
@@ -0,0 +1,107 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_PAYLOAD_HPP
+#define SCORE_SOCOM_PAYLOAD_HPP
+
+#include
+#include
+#include
+
+namespace score::socom {
+
+/// \brief Interface representing the Payload transferable by SOCom.
+/// \details The payload itself must be representable by a continuous Span of bytes.
+///
+/// The Payload has an optional header(), which is writable, but is not part of the data
+/// returned by data(). The optional header() is part of the same internal buffer, which also
+/// backs data().
+///
+/// The payload can internally look as follows:
+/// xxxxxxx SOME/IP_header | payload_data
+///
+/// Here | shows the position of the actual payload start in the buffer. Here "payload_data"
+/// will be returned with data().
+///
+/// This is needed for algorithms like the one for E2E, which require all data
+/// to be in contiguous memory and require an additional header for processing.
+/// \note When sending data over the wire, only data returned by data() shall be sent.
+class Payload {
+ public:
+ /// \brief Alias for a shared pointer to this interface.
+ using Sptr = std::shared_ptr;
+
+ /// \brief Alias for a data byte.
+ using Byte = std::byte;
+
+ /// \brief Alias for payload data.
+ using Span = score::cpp::span;
+
+ /// \brief Alias for writable payload data.
+ using Writable_span = score::cpp::span;
+
+ Payload() = default;
+ virtual ~Payload() = default;
+ Payload(Payload const&) = delete;
+ Payload(Payload&&) = delete;
+ Payload& operator=(Payload const&) = delete;
+ Payload& operator=(Payload&&) = delete;
+
+ /// \brief Retrieves the payload data.
+ /// \return Span of payload data.
+ [[nodiscard]] virtual Span data() const noexcept = 0;
+
+ /// \brief Retrieves the header data.
+ /// \return Span of header data.
+ [[nodiscard]] virtual Span header() const noexcept = 0;
+
+ /// \brief Retrieves the header data.
+ /// \return Writable span of header data.
+ [[nodiscard]] virtual Writable_span header() noexcept = 0;
+};
+
+/// \brief An empty payload instance, which may be used as default value for the payload parameter.
+/// \return A pointer to a Payload object.
+extern Payload::Sptr empty_payload();
+
+/// \brief Operator == for Payload.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of equality, otherwise false.
+bool operator==(Payload const& lhs, Payload const& rhs);
+
+/// \brief Operator != for Payload.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of inequality, otherwise false.
+bool operator!=(Payload const& lhs, Payload const& rhs);
+
+/// \brief Interface representing a writable payload, which can be allocated by the recipient for
+/// zero copy operations.
+///
+/// The recipient is responsible for allocating enough data for the sender.
+class Writable_payload : public Payload {
+ public:
+ /// \brief Alias for a shared pointer to this interface.
+ using Sptr = std::shared_ptr;
+ /// \brief Alias for a unique pointer to this interface.
+ using Uptr = std::unique_ptr;
+
+ /// \brief Retrieves the writable payload data.
+ /// \return Span of payload data.
+ [[nodiscard]] virtual Writable_span wdata() noexcept = 0;
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_PAYLOAD_HPP
diff --git a/src/socom/include/score/socom/posix_credentials.hpp b/src/socom/include/score/socom/posix_credentials.hpp
new file mode 100644
index 00000000..c64bc606
--- /dev/null
+++ b/src/socom/include/score/socom/posix_credentials.hpp
@@ -0,0 +1,30 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_POSIX_CREDENTIALS_HPP
+#define SCORE_SOCOM_POSIX_CREDENTIALS_HPP
+
+#include
+namespace score::socom {
+
+/// \brief Posix_credentials.
+struct Posix_credentials final {
+ /// \brief user ID
+ ::uid_t uid;
+ /// \brief group ID
+ ::gid_t gid;
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_POSIX_CREDENTIALS_HPP
diff --git a/src/socom/include/score/socom/registry_string_view.hpp b/src/socom/include/score/socom/registry_string_view.hpp
new file mode 100644
index 00000000..ed7c795d
--- /dev/null
+++ b/src/socom/include/score/socom/registry_string_view.hpp
@@ -0,0 +1,198 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SRC_SOCOM_INCLUDE_SCORE_SOCOM_REGISTRY_STRING_VIEW
+#define SRC_SOCOM_INCLUDE_SCORE_SOCOM_REGISTRY_STRING_VIEW
+
+#include
+#include
+
+namespace score::socom {
+
+///
+/// \class Registry_string_view
+///
+/// \brief Registry_string_view is an unmodifiable view of a string held in String_registry.
+/// For performance reasons the comparison between instances of Registry_string_view is done
+/// using simple pointer equality check and a check against other String_views is done by
+/// character comparison.
+/// It is the programmer's responsibility to ensure that Registry_string_view does not
+/// outlive the String_registry it is referring to.
+///
+class Registry_string_view final {
+ friend class String_registry;
+
+ public:
+ using const_pointer = typename std::string_view::const_pointer;
+ using const_iterator = typename std::string_view::const_iterator;
+ using difference_type = typename std::string_view::difference_type;
+ using size_type = typename std::string_view::size_type;
+ // npos is a constant representing the largest possible value of size_type, because of
+ // signed-to-unsigned implicit conversion. It is used as an "end of string" or "not found"
+ // indicator. More details: https://en.cppreference.com/w/cpp/string/basic_string/npos
+ static constexpr size_type npos = static_cast(-1);
+
+ ///
+ /// \brief Copy constructor (default)
+ ///
+ constexpr Registry_string_view(Registry_string_view const& other) noexcept = default;
+
+ ///
+ /// \brief Move constructor (default)
+ ///
+ constexpr Registry_string_view(Registry_string_view&& other) noexcept = default;
+
+ ///
+ /// \brief Destructor (default)
+ ///
+ ~Registry_string_view() noexcept = default;
+
+ ///
+ /// \brief Copy assignment operator (default)
+ ///
+ constexpr Registry_string_view& operator=(Registry_string_view const& view) & noexcept =
+ default;
+
+ ///
+ /// \brief Move assignment operator (default)
+ ///
+ constexpr Registry_string_view& operator=(Registry_string_view&& view) & noexcept = default;
+
+ ///
+ /// \brief Get string data
+ ///
+ constexpr const_pointer data() const noexcept { return m_string_view.data(); }
+
+ ///
+ /// \brief Get string length
+ ///
+ constexpr size_type length() const noexcept { return m_string_view.length(); }
+
+ ///
+ /// \brief Get string length
+ ///
+ constexpr size_type size() const noexcept { return m_string_view.size(); }
+
+ ///
+ /// \brief Get whether the string is empty
+ ///
+ constexpr bool empty() const noexcept { return m_string_view.empty(); }
+
+ ///
+ /// \brief Const iterator to the beginning
+ ///
+ constexpr const_iterator begin() const noexcept { return data(); }
+
+ ///
+ /// \brief Const iterator to the beginning
+ ///
+ constexpr const_iterator cbegin() const noexcept { return data(); }
+
+ ///
+ /// \brief Iterator to end
+ ///
+ const_iterator end() const noexcept {
+ return std::next(data(), static_cast(length()));
+ }
+
+ ///
+ /// \brief Const iterator to the end
+ ///
+ const_iterator cend() const noexcept {
+ return std::next(data(), static_cast(length()));
+ }
+
+ constexpr std::string_view string_view() const noexcept { return m_string_view; }
+
+ private:
+ std::string_view m_string_view;
+
+ explicit constexpr Registry_string_view(std::string_view view)
+ : m_string_view{std::move(view)} {}
+};
+
+///
+/// \brief operator==
+///
+constexpr bool operator==(Registry_string_view lhs, Registry_string_view rhs) noexcept {
+ return (lhs.data() == rhs.data()) && (lhs.length() == rhs.length());
+}
+
+///
+/// \brief operator!=
+///
+constexpr bool operator!=(Registry_string_view lhs, Registry_string_view rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+///
+/// \brief operator<
+///
+constexpr bool operator<(Registry_string_view lhs, Registry_string_view rhs) noexcept {
+ return lhs.string_view() < rhs.string_view();
+}
+
+///
+/// \brief operator<=
+///
+constexpr bool operator<=(Registry_string_view lhs, Registry_string_view rhs) noexcept {
+ return lhs.string_view() <= rhs.string_view();
+}
+
+///
+/// \brief operator>
+///
+constexpr bool operator>(Registry_string_view lhs, Registry_string_view rhs) noexcept {
+ return lhs.string_view() > rhs.string_view();
+}
+
+///
+/// \brief operator>=
+///
+constexpr bool operator>=(Registry_string_view lhs, Registry_string_view rhs) noexcept {
+ return lhs.string_view() >= rhs.string_view();
+}
+
+///
+/// \brief operator<<
+/// Overload of << to write the string from a Registry_string_view to ostream.
+///
+/// \return Reference to the ostream object used for writing, to enable chaining of << operations
+/// as usual.
+///
+inline std::ostream& operator<<(std::ostream& os, Registry_string_view v) {
+ return os.write(v.data(), static_cast(v.length()));
+}
+
+} // namespace score::socom
+
+namespace std {
+
+/// \brief std::hash specialization for Registry_string_view
+///
+/// \return Hash value for the given Registry_string_view
+///
+template <>
+struct hash<::score::socom::Registry_string_view> {
+ size_t operator()(::score::socom::Registry_string_view const& sv) const noexcept {
+ // For the conversion of Registry_string_view data pointer to intptr_t, the reinterpret_cast
+ // is necessary.
+ // Pointer-to-integer conversion is required for memory address hashing in this case.
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return std::hash{}(reinterpret_cast(sv.data()));
+ }
+};
+
+} // namespace std
+
+#endif // SRC_SOCOM_INCLUDE_SCORE_SOCOM_REGISTRY_STRING_VIEW
diff --git a/src/socom/include/score/socom/runtime.hpp b/src/socom/include/score/socom/runtime.hpp
new file mode 100644
index 00000000..9adba5f8
--- /dev/null
+++ b/src/socom/include/score/socom/runtime.hpp
@@ -0,0 +1,331 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_RUNTIME_HPP
+#define SCORE_SOCOM_RUNTIME_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+
+/// \brief Service bridge identification.
+class Bridge_identity {
+ public:
+ /// \brief Creates instance of Bridge_identity.
+ /// \param instance Reference instance.
+ /// \tparam T Type of instance.
+ /// \return Bridge_identity object.
+ template
+ static Bridge_identity make(T const& instance) {
+ return Bridge_identity{static_cast(&instance)};
+ }
+
+ /// \brief Operator == for Bridge_identity.
+ /// \param lhs Bridge_identity to compare.
+ /// \param rhs Bridge_identity to compare.
+ /// \return True in case of equality, otherwise false.
+ friend bool operator==(Bridge_identity lhs, Bridge_identity rhs) noexcept {
+ return lhs.m_identity == rhs.m_identity;
+ }
+
+ private:
+ explicit Bridge_identity(void const* identity) : m_identity{identity} {}
+
+ void const* m_identity;
+};
+
+/// \brief Operator != for Bridge_identity.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of inequality, otherwise false.
+inline bool operator!=(Bridge_identity const& lhs, Bridge_identity const& rhs) {
+ return !(lhs == rhs);
+}
+
+/// \brief Interface class for Find_subscription RAII type (see Runtime).
+class Find_subscription_handle {
+ public:
+ Find_subscription_handle() = default;
+ virtual ~Find_subscription_handle() = default;
+
+ Find_subscription_handle(Find_subscription_handle const&) = delete;
+ Find_subscription_handle(Find_subscription_handle&&) = delete;
+
+ Find_subscription_handle& operator=(Find_subscription_handle const&) = delete;
+ Find_subscription_handle& operator=(Find_subscription_handle&&) = delete;
+};
+
+/// \brief Interface class for Service_bridge_registration RAII type (see Runtime).
+class Service_bridge_registration_handle {
+ public:
+ Service_bridge_registration_handle() = default;
+ virtual ~Service_bridge_registration_handle() = default;
+
+ Service_bridge_registration_handle(Service_bridge_registration_handle const&) = delete;
+ Service_bridge_registration_handle(Service_bridge_registration_handle&&) = delete;
+
+ Service_bridge_registration_handle& operator=(Service_bridge_registration_handle const&) =
+ delete;
+ Service_bridge_registration_handle& operator=(Service_bridge_registration_handle&&) = delete;
+
+ /// \brief Getter for Bridge_identity.
+ /// \return Bridge_identity object.
+ [[nodiscard]] virtual Bridge_identity get_identity() const = 0;
+};
+
+/// \brief Interface class for Service_request RAII type (see Runtime).
+class Service_request_handle {
+ public:
+ Service_request_handle() = default;
+ virtual ~Service_request_handle() = default;
+
+ Service_request_handle(Service_request_handle const&) = delete;
+ Service_request_handle(Service_request_handle&&) = delete;
+
+ Service_request_handle& operator=(Service_request_handle const&) = delete;
+ Service_request_handle& operator=(Service_request_handle&&) = delete;
+};
+
+/// \brief RAII object that represents an active find service subscription, see
+/// Runtime::subscribe_find_service().
+using Find_subscription = std::unique_ptr;
+/// \brief RAII object that represents a service bridge registration at the runtime, see
+/// Runtime::register_service_bridge()
+using Service_bridge_registration = std::unique_ptr;
+/// \brief RAII object that represents an service request from the runtime to a service bridge, see
+/// Runtime::register_service_bridge().
+using Service_request = std::unique_ptr;
+
+/// \brief [[deprecated]] Find service result type, see Runtime::subscribe_find_service().
+using Find_result_container = std::vector;
+
+/// \brief Status of reported service.
+enum class Find_result_status : std::uint8_t {
+ added, ///< A new service is found.
+ deleted ///< A service is removed.
+};
+
+/// \brief [[deprecated]] Find service result indication callback type, see
+/// Runtime::subscribe_find_service().
+using Find_result_callback = std::function;
+
+/// \brief Find service result indication callback type, see Runtime::subscribe_find_service().
+using Find_result_change_callback = std::function;
+
+/// \brief Subscribe_find_service interface type signature.
+using Subscribe_find_service_function = std::function)>;
+
+/// \brief Request_service interface type signature.
+using Request_service_function =
+ std::function;
+
+/// \brief Interface that provides access to the service oriented communication (SOCom) middleware.
+/// \details SOCom implements a client-service-server based architectural pattern.
+/// A service is an instance (Service_instance) of an interface (Service_interface).
+/// A server provides a service.
+/// Clients use services.
+/// The service pattern makes client and server independent from concrete instances of each other
+/// (loose coupling). Depending on their availability, SOCom performs the dependency resolution
+/// client/server connection and disconnection at runtime.
+
+/// A service interface supports the following communication patterns:
+/// - method call (1:1)
+/// - client-server-client
+/// - client-server
+/// - event, also known as publish/subscribe (1:n)
+/// - server-clients
+class Runtime {
+ public:
+ /// \brief Alias for an unique pointer to this interface.
+ using Uptr = std::unique_ptr;
+
+ Runtime() = default;
+ virtual ~Runtime() noexcept = default;
+ Runtime(Runtime const&) = delete;
+ Runtime(Runtime&&) = delete;
+ Runtime& operator=(Runtime const&) = delete;
+ Runtime& operator=(Runtime&&) = delete;
+
+ /// \brief Creates a new client connector.
+ /// \details Returns a new instance Client_connector registered as a service user for the
+ /// service defined by the configuration.interface and instance parameters to the SOCom service
+ /// registry.
+ ///
+ /// If the first client connector for [configuration, instance] is created and the requested
+ /// service is locally not present, make_client_connector() calls
+ /// request_service(configuration, instance) on every bridge (already registered or registered
+ /// later) and stores the Service_request RAII objects.
+ ///
+ /// If the last client connector for [configuration, instance] is destroyed,
+ /// make_client_connector() deletes all associated service bridge Service_request RAII objects.
+ /// \param configuration Service interface configuration.
+ /// \param instance Service instance.
+ /// \param callbacks User callbacks to be called based on the internal states.
+ /// \return A pointer to a Client_connector instance in case of successful operation, otherwise
+ /// an error.
+ /// \note Construction_error::callback_missing is returned if any of the callbacks is not set.
+ /// \note This method sets the values returned from getuid() and getpid() (unistd.h) as
+ /// credentials of the returned Client_connector.
+ [[nodiscard]]
+ virtual Result make_client_connector(
+ Service_interface_definition configuration, Service_instance instance,
+ Client_connector::Callbacks callbacks) noexcept = 0;
+
+ /// \brief Creates a new client connector.
+ /// \details This method behaves the same as the make_client_connector() above.
+ /// Additionally, custom posix credentials can be passed.
+ /// \param configuration Service interface configuration.
+ /// \param instance Service instance.
+ /// \param callbacks User callbacks to be called based on the internal states.
+ /// \param credentials Posix credentials to be set for the client connector.
+ /// \return A pointer to a Client_connector instance in case of successful operation, otherwise
+ /// an error.
+ /// \note Construction_error::callback_missing is returned if any of the callbacks is not set.
+ [[nodiscard]]
+ virtual Result make_client_connector(
+ Service_interface_definition configuration, Service_instance instance,
+ Client_connector::Callbacks callbacks, Posix_credentials const& credentials) noexcept = 0;
+
+ /// \brief Creates a new server connector.
+ /// \details Returns a new instance of Disabled_server_connector registered as a service
+ /// provider for the service defined by the configuration.interface and instance parameters to
+ /// the SOCom service registry if this service does not exist in the runtime service registry
+ /// yet.
+ ///
+ /// Logs an error and returns Construction_error::duplicate_service if a service defined by the
+ /// configuration.interface and instance parameters is already registered in the runtime service
+ /// registry.
+
+ /// Returns Construction_error::callback_missing if any of the callbacks is not set.
+ /// \param configuration Service interface configuration.
+ /// \param instance Service instance.
+ /// \param callbacks User callbacks to be called based on the internal states.
+ /// \return A pointer to a server connector instance in case of successful operation, otherwise
+ /// an error.
+ /// \note This method sets the values returned from getuid() and getpid() (unistd.h) as
+ /// credentials of the returned Disabled_server_connector.
+ [[nodiscard]]
+ virtual Result make_server_connector(
+ Server_service_interface_definition configuration, Service_instance instance,
+ Disabled_server_connector::Callbacks callbacks) noexcept = 0;
+
+ /// \brief Creates a new server connector.
+ /// \details This method behaves the same as the make_server_connector() above.
+ /// Additionally, custom posix credentials can be passed.
+ /// \param configuration Service interface configuration.
+ /// \param instance Service instance.
+ /// \param callbacks User callbacks to be called based on the internal states.
+ /// \param credentials Posix credentials to be set for the server connector.
+ /// \return A pointer to a server connector instance in case of successful operation, otherwise
+ /// an error.
+ [[nodiscard]]
+ virtual Result make_server_connector(
+ Server_service_interface_definition configuration, Service_instance instance,
+ Disabled_server_connector::Callbacks callbacks,
+ Posix_credentials const& credentials) noexcept = 0;
+
+ /// \brief Offers the same functionality as the subscribe_find_service() below.
+ /// \details The complete list of currently available services is passed into the callback on
+ /// every change.
+ ///
+ /// If the set of known services matching the parameters interface and instance changes compared
+ /// to the last invocation of callback on_result_set, on_result_set is called with the complete
+ /// list of currently available services.
+ /// \param on_result_set_change Callback function.
+ /// \param interface Service interface.
+ /// \param instance Optional service instance.
+ /// \return Object that represents an active find service subscription.
+ [[nodiscard]] [[deprecated(
+ "Removed due to complexity. Use Client_connectors Service_state_change_callback instead.")]]
+ virtual Find_subscription subscribe_find_service(
+ Find_result_callback on_result_set_change, Service_interface_identifier const& interface,
+ std::optional instance) noexcept = 0;
+
+ /// \brief Calls on_result_change when a new service is found or a service is removed.
+ /// \note Interface and instance are used to filter for specific services.
+ /// \details Immediately reports the all currently known service instances matching the given
+ /// interface and instance to the callback on_result_change and returns a RAII object
+ /// representing this find subscription.
+ ///
+ /// If the set of known services matching the parameters interface and instance changes compared
+ /// to the last invocation of the callback on_result_change, on_result_change is called
+ /// with the new set of known service instances.
+ ///
+ /// If the object representing a find subscription is released, then any further changes are no
+ /// longer indicated through the callback on_result_change.
+ ///
+ /// If the parameter instance has no value, all instances matching the interface are part of the
+ /// result.
+ ///
+ /// If the callback on_result_change is nullptr, find subscription is not performed and the
+ /// callback on_result_change is never called.
+ ///
+ /// The method subscribe_find_service(interface, instance) is called on every already registered
+ /// or later registered bridge and the Service_request RAII objects are stored.
+ ///
+ /// If the last find service subscription for [interface, instance] is destroyed,
+ /// subscribe_find_service() deletes all associated find service subscription RAII objects.
+ ///
+ /// A service bridge contributes to the result-set of find service subscriptions by calling the
+ /// Find_result_change_callback for the specific bridge which is part of the
+ /// subscribe_find_service interface. Thus changes on the set of known service instances must be
+ /// indicated as locally created services. Duplicate services indicate a system configuration
+ /// error.
+ ///
+ /// If the one and only find service subscriber is a bridge that indicates the existence of the
+ /// parameter identity, then no find service request forwarding to the respective bridge
+ /// is active.
+ /// \param on_result_change Callback function.
+ /// \param interface Service interface.
+ /// \param instance Service instance.
+ /// \param identity Optional bridge identity.
+ /// \return Object that represents an active find service subscription.
+ [[nodiscard]] [[deprecated(
+ "Removed due to complexity. Use Client_connectors Service_state_change_callback instead.")]]
+ virtual Find_subscription subscribe_find_service(
+ Find_result_change_callback on_result_change,
+ std::optional interface,
+ std::optional instance,
+ std::optional identity) noexcept = 0;
+
+ /// \brief Registers a bridge which transports events or method calls over an IPC channel.
+ /// \param identity Bridge identity.
+ /// \param subscribe_find_service Function to call in order to search for services.
+ /// \param request_service Function to call if the requested service is not present locally.
+ /// \return A registration RAII object in case of successful operation, otherwise an error.
+ /// \note Construction_error::callback_missing is returned if any of the callbacks is not set.
+ [[nodiscard]]
+ virtual Result register_service_bridge(
+ Bridge_identity identity, Subscribe_find_service_function subscribe_find_service,
+ Request_service_function request_service) noexcept = 0;
+};
+
+/// \brief Function to instantiate a Runtime object.
+/// \param logger Logger for logging messages.
+/// \return Pointer to Runtime object.
+Runtime::Uptr create_runtime();
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_RUNTIME_HPP
diff --git a/src/socom/include/score/socom/server_connector.hpp b/src/socom/include/score/socom/server_connector.hpp
new file mode 100644
index 00000000..88c5e892
--- /dev/null
+++ b/src/socom/include/score/socom/server_connector.hpp
@@ -0,0 +1,227 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_SERVER_CONNECTOR_HPP
+#define SCORE_SOCOM_SERVER_CONNECTOR_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+
+class Disabled_server_connector;
+class Enabled_server_connector;
+
+/// \brief Function type for indicating an event subscription state change to the service provider.
+using Event_subscription_change_callback =
+ score::cpp::move_only_function;
+
+/// \brief Function type for indicating an event update request to the service provider.
+using Event_request_update_callback =
+ score::cpp::move_only_function;
+
+/// \brief Function type for processing any client side method invocation.
+using Method_call_credentials_callback = score::cpp::move_only_function;
+
+/// \brief Function type for indicating a method call payload request to the service provider.
+using Method_call_payload_allocate_callback =
+ score::cpp::move_only_function(Enabled_server_connector&,
+ Method_id)>;
+
+class Configuration_getter {
+ public:
+ virtual ~Configuration_getter() = default;
+
+ [[nodiscard]]
+ virtual Server_service_interface_definition const& get_configuration() const noexcept = 0;
+ [[nodiscard]]
+ virtual Service_instance const& get_service_instance() const noexcept = 0;
+};
+
+/// \brief Interface for applications to use a service (server-role).
+/// \details This interface represents a Server_connector not visible to any Client_connector(s).
+/// After destruction no registered callbacks are called anymore.
+/// All user callbacks must not block and shall return quickly (simple algorithms only).
+class Disabled_server_connector : public Configuration_getter {
+ public:
+ /// \brief Alias for an unique pointer to this interface.
+ using Uptr = std::unique_ptr;
+
+ Disabled_server_connector() = default;
+ virtual ~Disabled_server_connector() noexcept = default;
+
+ Disabled_server_connector(Disabled_server_connector const&) = delete;
+ Disabled_server_connector(Disabled_server_connector&&) = delete;
+
+ Disabled_server_connector& operator=(Disabled_server_connector const&) = delete;
+ Disabled_server_connector& operator=(Disabled_server_connector&&) = delete;
+
+ /// \brief Server_Connector callback interface needed at Server_connector construction, see
+ /// Runtime::make_server_connector().
+ ///
+ /// \details All user callbacks must not block and shall return quickly (simple algorithms
+ /// only). No callback is allowed to destroy the Server_connector, otherwise it will result in a
+ /// deadlock. If a deadlock situation is detected, a warning will be logged and the application
+ /// terminated.
+ struct Callbacks {
+ /// \brief Callback is called on any client side method invocation.
+ Method_call_credentials_callback on_method_call;
+
+ /// \brief Callback is called if an event is subscribed by the first Client_connector or
+ /// unsubscribed by the last Client_connector.
+ Event_subscription_change_callback on_event_subscription_change;
+
+ /// \brief Callback is called if an event update is requested by any Client_connector.
+ /// \details On a call to callback on_event_update_request(), the Server application calls
+ /// update_requested_event() or update_event() for the requested event as follows:
+ /// - update_requested_event() is called if no new data is available from the
+ /// application (indicate current state only to requesting clients);
+ /// - update_event() is called if new data is available from the application (indicate new
+ /// state to all clients).
+ Event_request_update_callback on_event_update_request;
+
+ /// \brief Callback is called to allocate method call payloads.
+ Method_call_payload_allocate_callback on_method_call_payload_allocate;
+ };
+
+ /// \brief Makes the service available to clients.
+ /// \details Changes the connector to state 'Enabled' and converts it to an
+ /// Enabled_server_connector. Registers the Enabled_server_connector at the SOCom service
+ /// registry, connects each matching registered Client_connector to this instance and calls the
+ /// callback on_service_state_change(Service_state::available, server_configuration) of each
+ /// connected Client_connector instance.
+ ///
+ /// Server_connector instance callbacks may be called after entering enable().
+ /// \param connector Disabled server connector.
+ /// \return An enabled server connector.
+ [[nodiscard]]
+ static std::unique_ptr enable(
+ std::unique_ptr connector);
+
+ protected:
+ /// \cond INTERNAL
+ virtual Enabled_server_connector* enable() = 0;
+ /// \endcond
+};
+
+/// \brief Interface for applications to use a service (server-role).
+/// \details This interface represents an enabled Server_connector, thus it is registered by the
+/// SOCom service registry and available to connected Client_connector(s).
+///
+/// If a client calls Client_connector::call_method, then the callback on_method_called() is called.
+///
+/// If the client-aggregated need of an event changes, then callback on_event_subscription_change()
+/// is called.
+///
+/// If a client requests the current value of an event, then callback on_event_update_request() is
+/// called.
+///
+/// If the passed parameter server_id is not valid (not contained in
+/// Server_service_interface_definition), service API calls have no effect and return
+/// Server_connector_error::logic_error_id_out_of_range.
+class Enabled_server_connector : public Configuration_getter {
+ public:
+ /// \brief Alias for an unique pointer to this interface.
+ using Uptr = std::unique_ptr;
+
+ /// \brief Constructor.
+ Enabled_server_connector() = default;
+
+ /// \brief Destructor.
+ /// \details Disconnects from Client_connectors and destroys the Server_connector. After
+ /// destruction no registered callbacks are called anymore.
+ ///
+ /// Implicitly calls disable() and deallocates the instance resources.
+ ///
+ /// Detects deadlocks which are caused by destroying the Client_connector from a running
+ /// Client_connector callback. When a deadlock is detected, the destructor shall log and
+ /// terminate the application.
+ virtual ~Enabled_server_connector() noexcept = default;
+
+ Enabled_server_connector(Enabled_server_connector const&) = delete;
+ Enabled_server_connector(Enabled_server_connector&&) = delete;
+
+ Enabled_server_connector& operator=(Enabled_server_connector const&) = delete;
+ Enabled_server_connector& operator=(Enabled_server_connector&&) = delete;
+
+ /// \brief Removes the connection to the clients.
+ /// \details Calls the callback on_service_state_change(Service_state::not_available) of
+ /// each connected Client_connector instances. It disconnects from all connected
+ /// Client_connector instances and blocks until all clients are disconnected.
+ /// \param connector Enabled server connector.
+ /// \return A disabled server connector.
+ [[nodiscard]]
+ static std::unique_ptr disable(
+ std::unique_ptr connector) noexcept;
+
+ /// \brief Allocates a payload for the given event ID.
+ ///
+ /// This requires a Client_connector to be subscribed to the event to which payload allocation
+ /// is delegated.
+ ///
+ /// \param event_id ID of the event for which a payload should be allocated.
+ /// \return A writable payload in case of successful operation, otherwise an error.
+ [[nodiscard]]
+ virtual Result> allocate_event_payload(
+ Event_id event_id) noexcept = 0;
+
+ /// \brief Distributes new event data to all subscribed Client_connectors.
+ /// \details Clears the list of event update requesters for the event server_id.
+ ///
+ /// Calls the callback on_event_update(client_id, payload) for each connected Client_connector
+ /// which is subscribed to event server_id.
+ /// \param server_id ID of the event.
+ /// \param payload Event data.
+ /// \return Void in case of successful operation, otherwise an error.
+ virtual Result update_event(Event_id server_id, Payload::Sptr payload) noexcept = 0;
+
+ /// \brief Distributes new event data to all event update requesting Client_connectors.
+ /// \details Clears the list of event update requesters for the event server_id.
+ ///
+ /// Calls the callback on_event_requested_update(client_id, payload) for each connected
+ /// Client_connector instance in a list of update requesters for event server_id.
+ /// \param server_id ID of the event.
+ /// \param payload Event data.
+ /// \return Void in case of successful operation, otherwise an error.
+ virtual Result update_requested_event(Event_id server_id,
+ Payload::Sptr payload) noexcept = 0;
+
+ /// \brief Retrieves the mode of the event server_id.
+ /// \details Returns the combined event subscription mode for event server_id, see
+ /// Client_connector::subscribe_event().
+ ///
+ /// Returns Event_mode::update_and_initial_value if any client has subscribed with
+ /// Event_mode::update_and_initial_value.
+ ///
+ /// Returns Event_mode::update if no Client_connector instance has subscribed to this event yet.
+ /// \param server_id ID of the event.
+ /// \return An event mode in case of successful operation, otherwise an error.
+ [[nodiscard]] virtual Result get_event_mode(Event_id server_id) const noexcept = 0;
+
+ protected:
+ /// \cond INTERNAL
+ virtual Disabled_server_connector* disable() noexcept = 0;
+ /// \endcond
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_SERVER_CONNECTOR_HPP
diff --git a/src/socom/include/score/socom/service_interface_definition.hpp b/src/socom/include/score/socom/service_interface_definition.hpp
new file mode 100644
index 00000000..61eaddc4
--- /dev/null
+++ b/src/socom/include/score/socom/service_interface_definition.hpp
@@ -0,0 +1,108 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SRC_SOCOM_INCLUDE_SCORE_SOCOM_SERVICE_INTERFACE_DEFINITION
+#define SRC_SOCOM_INCLUDE_SCORE_SOCOM_SERVICE_INTERFACE_DEFINITION
+
+#include
+#include
+#include
+
+namespace score::socom {
+
+/// description: Strong type for forced proper construction.
+enum class Num_of_events : std::size_t {};
+
+/// description: Strong type for forced proper construction.
+enum class Num_of_methods : std::size_t {};
+
+inline Num_of_events to_num_of_events(std::size_t const value) noexcept {
+ return static_cast(value);
+}
+
+inline Num_of_methods to_num_of_methods(std::size_t const value) noexcept {
+ return static_cast(value);
+}
+
+/// \brief Service interface configuration data structure for Client_connector instances.
+/// \details This type, which is used by Runtime::make_client_connector(), allows an optional member
+/// configuration.
+struct Service_interface_definition final {
+ /// \brief Constructor for default use-case.
+ /// \param sif Service interface identification information.
+ /// \param methods Methods of the service interface.
+ /// \param events Events of the service interface.
+ Service_interface_definition(Service_interface_identifier sif, Num_of_methods num_of_methods,
+ Num_of_events num_of_events);
+
+ /// \brief Constructor without methods and events.
+ /// \details Client_connectors which have no member configuration must use the provided
+ /// Server_service_interface_definition configuration.
+ /// \param sif Service interface identification information.
+ explicit Service_interface_definition(Service_interface_identifier sif);
+
+ Service_interface_definition(Service_interface_definition const&) = default;
+ Service_interface_definition(Service_interface_definition&&) noexcept = default;
+
+ ~Service_interface_definition() noexcept = default;
+
+ Service_interface_definition& operator=(Service_interface_definition const&) = delete;
+ Service_interface_definition& operator=(Service_interface_definition&&) = delete;
+
+ /// \brief Service interface identification information.
+ Service_interface_identifier const interface;
+ std::size_t num_methods{0U};
+ std::size_t num_events{0U};
+};
+
+bool operator==(Service_interface_definition const& lhs, Service_interface_definition const& rhs);
+
+bool operator<(Service_interface_definition const& lhs, Service_interface_definition const& rhs);
+
+/// \brief Service interface configuration data structure for Server_connector instances.
+/// \details This type, which is used by Runtime::make_server_connector(), enforces a member
+/// configuration.
+class Server_service_interface_definition final {
+ Service_interface_definition m_configuration;
+
+ public:
+ /// \brief Constructor.
+ /// \param sif Service interface identification information.
+ /// \param methods Methods of the service interface.
+ /// \param events Events of the service interface.
+ Server_service_interface_definition(Service_interface_identifier const& sif,
+ Num_of_methods num_of_methods, Num_of_events num_of_events);
+
+ Server_service_interface_definition(Server_service_interface_definition const& rhs);
+ Server_service_interface_definition(Server_service_interface_definition&& rhs) noexcept;
+
+ ~Server_service_interface_definition() noexcept = default;
+
+ Server_service_interface_definition& operator=(Server_service_interface_definition const&) =
+ delete;
+ Server_service_interface_definition& operator=(Server_service_interface_definition&&) = delete;
+
+ // Service_interface_definition
+ /// \brief Retrieves the configuration by implicitly converting an instance to
+ /// Service_interface_definition.
+ /// \return The stored configuration.
+ operator Service_interface_definition() const;
+
+ std::size_t get_num_methods() const noexcept;
+ std::size_t get_num_events() const noexcept;
+ Service_interface_identifier const& get_interface() const noexcept;
+};
+
+} // namespace score::socom
+
+#endif // SRC_SOCOM_INCLUDE_SCORE_SOCOM_SERVICE_INTERFACE_DEFINITION
diff --git a/src/socom/include/score/socom/service_interface_identifier.hpp b/src/socom/include/score/socom/service_interface_identifier.hpp
new file mode 100644
index 00000000..3d1d5901
--- /dev/null
+++ b/src/socom/include/score/socom/service_interface_identifier.hpp
@@ -0,0 +1,191 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SRC_SOCOM_INCLUDE_SCORE_SOCOM_SERVICE_INTERFACE_IDENTIFIER
+#define SRC_SOCOM_INCLUDE_SCORE_SOCOM_SERVICE_INTERFACE_IDENTIFIER
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+
+/// Service instance identification information
+class Service_instance final {
+ public:
+ using Id = Registry_string_view;
+
+ /// String-based service instance identifier.
+ Id id;
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ explicit Service_instance(Id new_id) noexcept : id{new_id} {}
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ explicit Service_instance(std::string_view new_id)
+ : id{score::socom::instance_id_registry().insert(new_id).first} {}
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ /// \param is_static_string_literal Tag to indicate that the provided string is a static string
+ /// literal.
+ Service_instance(std::string_view new_id, Literal_tag is_static_string_literal)
+ : id{score::socom::instance_id_registry().insert(new_id, is_static_string_literal).first} {}
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ explicit Service_instance(std::string&& new_id)
+ : id{score::socom::instance_id_registry().insert(std::move(new_id)).first} {}
+};
+
+/// \brief Operator == for Service_instance.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of equality, otherwise false.
+inline bool operator==(Service_instance const& lhs, Service_instance const& rhs) {
+ return lhs.id == rhs.id;
+}
+
+/// \brief Operator < for Service_instance.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of lhs is less than rhs, otherwise false.
+inline bool operator<(Service_instance const& lhs, Service_instance const& rhs) {
+ return lhs.id < rhs.id;
+}
+
+/// \brief Service interface identification information.
+struct Service_interface_identifier {
+ public:
+ /// \brief Alias for a service interface identifier.
+ using Id = Registry_string_view;
+
+ /// \brief Service interface version type.
+ struct Version {
+ /// \brief Major version information.
+ /// \note Major version must match exactly for service interface compatibility.
+ std::uint16_t major;
+ /// \brief Minor version information.
+ /// \note Minor version of Client_connector is less or equal than the minor version of
+ /// Server_connector for service interface compatibility.
+ std::uint16_t minor;
+ };
+
+ /// \brief Service interface identifier.
+ Id id;
+
+ /// \brief Service interface version information.
+ Version version;
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ /// \param new_version Version of the service interface.
+ Service_interface_identifier(Id new_id, Version new_version) noexcept
+ : id{new_id}, version{new_version} {}
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ /// \param new_version Version of the service interface.
+ Service_interface_identifier(std::string_view new_id, Version new_version)
+ : id{score::socom::service_id_registry().insert(new_id).first}, version{new_version} {}
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ /// \param is_static_string_literal Tag to indicate that the provided string is a static string
+ /// literal.
+ /// \param new_version Version of the service interface.
+ Service_interface_identifier(std::string_view new_id, Literal_tag is_static_string_literal,
+ Version new_version)
+ : id{score::socom::service_id_registry().insert(new_id, is_static_string_literal).first},
+ version{new_version} {}
+
+ /// \brief Constructor.
+ /// \param new_id ID of the service interface.
+ /// \param new_version Version of the service interface.
+ Service_interface_identifier(std::string&& new_id, Version new_version)
+ : id{score::socom::service_id_registry().insert(std::move(new_id)).first},
+ version{new_version} {}
+};
+
+/// \brief Operator == for Service_interface_identifier::Version.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of equality, otherwise false.
+inline bool operator==(Service_interface_identifier::Version const& lhs,
+ Service_interface_identifier::Version const& rhs) {
+ return (std::tie(lhs.major, lhs.minor) == std::tie(rhs.major, rhs.minor));
+}
+
+/// \brief Operator < for Service_interface_identifier::Version.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case the contents of lhs are lexicographically less than the contents of rhs,
+/// otherwise false.
+inline bool operator<(Service_interface_identifier::Version const& lhs,
+ Service_interface_identifier::Version const& rhs) {
+ return (std::tie(lhs.major, lhs.minor) < std::tie(rhs.major, rhs.minor));
+}
+
+/// \brief Operator == for Service_interface_identifier.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case of equality, otherwise false.
+inline bool operator==(Service_interface_identifier const& lhs,
+ Service_interface_identifier const& rhs) {
+ return (std::tie(lhs.id, lhs.version) == std::tie(rhs.id, rhs.version));
+}
+
+/// \brief Operator < for Service_interface_identifier.
+/// \param lhs Left-hand side of operator.
+/// \param rhs Right-hand side of operator.
+/// \return True in case the contents of lhs are lexicographically less than the contents of rhs,
+/// otherwise false.
+inline bool operator<(Service_interface_identifier const& lhs,
+ Service_interface_identifier const& rhs) {
+ return (std::tie(lhs.id, lhs.version) < std::tie(rhs.id, rhs.version));
+}
+
+} // namespace score::socom
+
+/// \brief std::hash specialization for Service_instance
+///
+/// \return Hash value for the given Service_instance
+///
+template <>
+struct std::hash {
+ std::size_t operator()(score::socom::Service_instance const& s) const noexcept {
+ return std::hash{}(s.id);
+ }
+};
+
+/// \brief std::hash specialization for Service_interface_identifier
+///
+/// \return Hash value for the given Service_interface_identifier
+///
+template <>
+struct std::hash {
+ std::size_t operator()(score::socom::Service_interface_identifier const& s) const noexcept {
+ std::size_t const h1 = std::hash{}(s.id);
+ std::size_t const h2 = std::hash{}(s.version.major);
+ std::size_t const h3 = std::hash{}(s.version.minor);
+ auto const hash = h1 ^ (h2 << 1) ^ (h3 << 2);
+ return hash;
+ }
+};
+
+#endif // SRC_SOCOM_INCLUDE_SCORE_SOCOM_SERVICE_INTERFACE_IDENTIFIER
diff --git a/src/socom/include/score/socom/string_registry.hpp b/src/socom/include/score/socom/string_registry.hpp
new file mode 100644
index 00000000..de13ba81
--- /dev/null
+++ b/src/socom/include/score/socom/string_registry.hpp
@@ -0,0 +1,85 @@
+/********************************************************************************
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SRC_SOCOM_INCLUDE_SCORE_SOCOM_STRING_REGISTRY
+#define SRC_SOCOM_INCLUDE_SCORE_SOCOM_STRING_REGISTRY
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+
+/// \brief Tag to select StringView literal version
+struct Literal_tag {};
+
+///
+/// \class String_registry
+///
+/// \brief A central registry for strings to avoid copying and to facilitate cheap comparison.
+///
+class String_registry final {
+ public:
+ ///
+ /// \brief Insert a new StringView literal into the string registry.
+ ///
+ /// \param[in] new_string String to be added to the registry.
+ /// \param[in] (no name) Used only to select StringView literal version of the
+ /// method.
+ ///
+ /// \return String_view of the string in registry and boolean denoting whether the string was
+ /// newly added (true) or was present already (false).
+ ///
+ std::pair insert(std::string_view new_string,
+ Literal_tag /*is_static_string_literal*/) noexcept;
+
+ ///
+ /// \brief Insert a new StringView into the string registry.
+ ///
+ /// \param[in] new_string String to be added to the registry.
+ ///
+ /// \return String_view of the string in registry and boolean denoting whether the string was
+ /// newly added (true) or was present already (false).
+ ///
+ // NOLINTNEXTLINE(bugprone-exception-escape): If exception is thrown it shall be considered as
+ // fatal error and std::terminate is desired behavior.
+ std::pair insert(std::string_view new_string) noexcept;
+
+ ///
+ /// \brief Insert a new new std::string into the string registry.
+ ///
+ /// \param[in] new_string String to be added to the registry.
+ ///
+ /// \return String_view of the string in registry and boolean denoting whether the string was
+ /// newly added (true) or was present already (false).
+ ///
+ std::pair insert(std::string&& new_string) noexcept;
+
+ private:
+ std::unordered_set m_registered_strings;
+ std::forward_list m_dynamic_allocated;
+ std::mutex m_mutex;
+};
+
+String_registry& service_id_registry() noexcept;
+
+String_registry& instance_id_registry() noexcept;
+
+} // namespace score::socom
+
+#endif // SRC_SOCOM_INCLUDE_SCORE_SOCOM_STRING_REGISTRY
diff --git a/src/socom/include/score/socom/vector_payload.hpp b/src/socom/include/score/socom/vector_payload.hpp
new file mode 100644
index 00000000..11315ff8
--- /dev/null
+++ b/src/socom/include/score/socom/vector_payload.hpp
@@ -0,0 +1,62 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_VECTOR_PAYLOAD_HPP
+#define SCORE_SOCOM_VECTOR_PAYLOAD_HPP
+
+#include
+#include
+#include
+
+namespace score::socom {
+
+/// \brief Alias for payload data.
+using Vector_buffer = std::vector;
+
+/// \brief Creates a Vector_buffer from a list of unsigned integral type elements.
+/// \param args List of unsigned integral type elements to be included in the Vector_buffer.
+/// \return A Vector_buffer containing the provided elements.
+template
+Vector_buffer make_vector_buffer(Ts... args) noexcept {
+ static_assert((std::is_unsigned_v && ...),
+ "All arguments must be unsigned integral types.");
+ // TODO check that the types are not larger than Payload::Byte
+ return {static_cast(args)...};
+}
+
+/// \brief Creates a vector payload by moving the given data.
+/// \param buffer Payload data.
+/// \return A pointer to a Payload object.
+Payload::Sptr make_vector_payload(Vector_buffer buffer);
+
+/// \brief Creates a vector payload by moving the given data.
+/// \param header_size Size of header data.
+/// \param buffer Payload data.
+/// \return A pointer to a Payload object.
+Payload::Sptr make_vector_payload(std::size_t header_size, Vector_buffer buffer);
+
+Payload::Sptr make_vector_payload(std::size_t lead_offset, std::size_t header_size,
+ Vector_buffer buffer);
+
+/// \brief Creates vector payload from a container.
+/// \param container Reference container.
+/// \tparam C Container type.
+/// \return A pointer to a Payload object.
+template
+inline Payload::Sptr make_vector_payload(C const& container) {
+ return make_vector_payload(Vector_buffer{std::begin(container), std::end(container)});
+}
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_VECTOR_PAYLOAD_FACTORY_HPP
diff --git a/src/socom/mock/score/socom/callback_mocks.hpp b/src/socom/mock/score/socom/callback_mocks.hpp
new file mode 100644
index 00000000..5082b329
--- /dev/null
+++ b/src/socom/mock/score/socom/callback_mocks.hpp
@@ -0,0 +1,57 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_CALLBACK_MOCKS_HPP
+#define SCORE_SOCOM_CALLBACK_MOCKS_HPP
+
+#include
+
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+
+// Runtime callbacks
+using Find_result_change_callback_mock = ::testing::MockFunction;
+using Legacy_find_result_callback_mock = ::testing::MockFunction;
+
+// Client_connector callbacks
+using Service_state_change_callback_mock = Move_only_function_mock;
+using Event_update_callback_mock = Move_only_function_mock;
+using Event_payload_allocate_callback_mock =
+ Move_only_function_mock;
+
+// Server_connector callbacks
+using Event_subscription_change_callback_mock =
+ Move_only_function_mock;
+using Event_request_update_callback_mock = Move_only_function_mock;
+using Method_call_credentials_callback_mock =
+ Move_only_function_mock;
+using Method_call_payload_allocate_callback_mock =
+ Move_only_function_mock;
+
+// Method callbacks
+using Method_call_credentials_callback_mock =
+ Move_only_function_mock;
+using Method_reply_callback_mock = Move_only_function_mock;
+
+// Bridge callbacks
+using Subscribe_find_service_function_mock =
+ ::testing::MockFunction;
+using Request_service_function_mock = ::testing::MockFunction;
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_CALLBACK_MOCKS_HPP
diff --git a/src/socom/mock/score/socom/client_connector_mock.hpp b/src/socom/mock/score/socom/client_connector_mock.hpp
new file mode 100644
index 00000000..aef4b163
--- /dev/null
+++ b/src/socom/mock/score/socom/client_connector_mock.hpp
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_CLIENT_CONNECTOR_MOCK_HPP
+#define SCORE_SOCOM_CLIENT_CONNECTOR_MOCK_HPP
+
+#include
+
+#include
+
+namespace score::socom {
+
+class Client_connector_mock : public Client_connector {
+ public:
+ MOCK_METHOD(Result, subscribe_event, (Event_id client_id, Event_mode mode),
+ (const, noexcept, override));
+ MOCK_METHOD(Result, unsubscribe_event, (Event_id), (const, noexcept, override));
+ MOCK_METHOD(Result, request_event_update, (Event_id), (const, noexcept, override));
+ MOCK_METHOD(Result, call_method,
+ (Method_id, Payload::Sptr, Method_call_reply_data_opt),
+ (const, noexcept, override));
+ MOCK_METHOD(Result>, allocate_method_call_payload,
+ (Method_id method_id), (noexcept, override));
+ MOCK_METHOD(Result, get_peer_credentials, (), (const, noexcept, override));
+ MOCK_METHOD(Service_interface_definition const&, get_configuration, (),
+ (const, noexcept, override));
+ MOCK_METHOD(Service_instance const&, get_service_instance, (), (const, noexcept, override));
+ MOCK_METHOD(bool, is_service_available, (), (const, noexcept, override));
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_CLIENT_CONNECTOR_MOCK_HPP
diff --git a/src/socom/mock/score/socom/move_only_function_mock.hpp b/src/socom/mock/score/socom/move_only_function_mock.hpp
new file mode 100644
index 00000000..b4c88795
--- /dev/null
+++ b/src/socom/mock/score/socom/move_only_function_mock.hpp
@@ -0,0 +1,78 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_MOVE_ONLY_FUNCTION_MOCK_HPP
+#define SCORE_SOCOM_MOVE_ONLY_FUNCTION_MOCK_HPP
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace score::socom {
+
+namespace internal {
+
+template
+class Move_only_function_mock_base : public ::testing::MockFunction {
+ public:
+ using Base = ::testing::MockFunction;
+
+ using Base::Call;
+
+ template
+ Function_t as_function_impl() {
+ return [this](Args... args) -> Return_type {
+ if constexpr (std::is_void_v) {
+ this->Call(std::forward(args)...);
+ } else {
+ return this->Call(std::forward(args)...);
+ }
+ };
+ }
+
+ std::function AsStdFunction() {
+ return as_function_impl>();
+ }
+
+ Function as_function() { return as_function_impl(); }
+};
+
+} // namespace internal
+
+template
+class Move_only_function_mock;
+
+template
+class Move_only_function_mock
+ : public internal::Move_only_function_mock_base,
+ Return_type, Args...> {};
+
+template
+class Move_only_function_mock>
+ : public internal::Move_only_function_mock_base,
+ Return_type, Args...> {};
+
+template
+class Move_only_function_mock<
+ ::score::cpp::move_only_function>
+ : public internal::Move_only_function_mock_base<
+ ::score::cpp::move_only_function,
+ Return_type, Args...> {};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_MOVE_ONLY_FUNCTION_MOCK_HPP
diff --git a/src/socom/mock/score/socom/payload_mock.hpp b/src/socom/mock/score/socom/payload_mock.hpp
new file mode 100644
index 00000000..0163452a
--- /dev/null
+++ b/src/socom/mock/score/socom/payload_mock.hpp
@@ -0,0 +1,40 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_PAYLOAD_MOCK_HPP
+#define SCORE_SOCOM_PAYLOAD_MOCK_HPP
+
+#include
+
+#include
+
+namespace score::socom {
+
+class Payload_mock : public Payload {
+ public:
+ MOCK_METHOD(Span, data, (), (const, noexcept, override));
+ MOCK_METHOD(Span, header, (), (const, noexcept, override));
+ MOCK_METHOD(Writable_span, header, (), (noexcept, override));
+};
+
+class Writable_payload_mock : public score::socom::Writable_payload {
+ public:
+ MOCK_METHOD(Span, data, (), (const, noexcept, override));
+ MOCK_METHOD(Span, header, (), (const, noexcept, override));
+ MOCK_METHOD(Writable_span, header, (), (noexcept, override));
+ MOCK_METHOD(Writable_span, wdata, (), (noexcept, override));
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_PAYLOAD_MOCK_HPP
diff --git a/src/socom/mock/score/socom/runtime_mock.hpp b/src/socom/mock/score/socom/runtime_mock.hpp
new file mode 100644
index 00000000..60db69ea
--- /dev/null
+++ b/src/socom/mock/score/socom/runtime_mock.hpp
@@ -0,0 +1,62 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_RUNTIME_MOCK_HPP
+#define SCORE_SOCOM_RUNTIME_MOCK_HPP
+
+#include
+
+#include
+
+namespace score::socom {
+
+class Service_bridge_registration_handle_mock : public Service_bridge_registration_handle {
+ public:
+ // mock interface
+ MOCK_METHOD(Bridge_identity, get_identity, (), (const, override));
+};
+
+class Runtime_mock : public Runtime {
+ public:
+ // mock interface
+ MOCK_METHOD(Result, make_client_connector,
+ (Service_interface_definition, Service_instance, Client_connector::Callbacks),
+ (noexcept, override));
+ MOCK_METHOD(Result, make_client_connector,
+ (Service_interface_definition, Service_instance, Client_connector::Callbacks,
+ Posix_credentials const&),
+ (noexcept, override));
+ MOCK_METHOD((Result), make_server_connector,
+ (Server_service_interface_definition, Service_instance,
+ Disabled_server_connector::Callbacks),
+ (noexcept, override));
+ MOCK_METHOD((Result), make_server_connector,
+ (Server_service_interface_definition, Service_instance,
+ Disabled_server_connector::Callbacks, Posix_credentials const&),
+ (noexcept, override));
+ MOCK_METHOD(Find_subscription, subscribe_find_service,
+ (Find_result_callback, Service_interface_identifier const&,
+ std::optional),
+ (noexcept, override));
+ MOCK_METHOD(Find_subscription, subscribe_find_service,
+ (Find_result_change_callback, std::optional,
+ std::optional, std::optional),
+ (noexcept, override));
+ MOCK_METHOD(Result, register_service_bridge,
+ (Bridge_identity, Subscribe_find_service_function, Request_service_function),
+ (noexcept, override));
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_RUNTIME_MOCK_HPP
diff --git a/src/socom/mock/score/socom/server_connector_mock.hpp b/src/socom/mock/score/socom/server_connector_mock.hpp
new file mode 100644
index 00000000..3e624779
--- /dev/null
+++ b/src/socom/mock/score/socom/server_connector_mock.hpp
@@ -0,0 +1,42 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_SERVER_CONNECTOR_MOCK_HPP
+#define SCORE_SOCOM_SERVER_CONNECTOR_MOCK_HPP
+
+#include
+
+#include
+
+namespace score::socom {
+
+class Server_connector_mock : public Disabled_server_connector, public Enabled_server_connector {
+ public:
+ MOCK_METHOD(Enabled_server_connector*, enable, (), (noexcept, override));
+ MOCK_METHOD(Disabled_server_connector*, disable, (), (noexcept, override));
+ MOCK_METHOD(Result, update_event, (Event_id, Payload::Sptr), (noexcept, override));
+ MOCK_METHOD(Result, update_requested_event, (Event_id, Payload::Sptr),
+ (noexcept, override));
+ MOCK_METHOD(Result, get_event_mode, (Event_id), (const, noexcept, override));
+
+ MOCK_METHOD(Result>, allocate_event_payload,
+ (Event_id event_id), (noexcept, override));
+
+ MOCK_METHOD(Server_service_interface_definition const&, get_configuration, (),
+ (const, noexcept, override));
+ MOCK_METHOD(Service_instance const&, get_service_instance, (), (const, noexcept, override));
+};
+
+} // namespace score::socom
+
+#endif // SCORE_SOCOM_SERVER_CONNECTOR_MOCK_HPP
diff --git a/src/socom/src/client_connector_impl.cpp b/src/socom/src/client_connector_impl.cpp
new file mode 100644
index 00000000..ab42aa8b
--- /dev/null
+++ b/src/socom/src/client_connector_impl.cpp
@@ -0,0 +1,240 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#include "client_connector_impl.hpp"
+
+#include
+#include
+#include
+
+#include "messages.hpp"
+#include "runtime_impl.hpp"
+#include "score/socom/client_connector.hpp"
+#include "server_connector_impl.hpp"
+
+namespace score {
+namespace socom {
+namespace client_connector {
+
+Impl::Impl(Runtime_impl& runtime, Service_interface_definition configuration,
+ Service_instance instance, Client_connector::Callbacks callbacks,
+ Posix_credentials const& credentials)
+ : m_configuration{std::move(configuration)},
+ m_instance{std::move(instance)},
+ m_callbacks{std::move(callbacks)},
+ m_stop_block_token{
+ std::make_shared([this]() { m_stop_complete_promise.set_value(); })},
+ m_registration{runtime.register_connector(m_configuration, m_instance,
+ make_on_server_update_callback())},
+ m_credentials{credentials} {
+ assert(m_registration);
+}
+
+Impl::~Impl() noexcept {
+ {
+ std::lock_guard const lock{m_mutex};
+ m_stop_block_token.reset();
+ m_registration.reset();
+ m_server.reset();
+ }
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+
+ // death tests cannot contribute to code coverage
+ auto const log_on_deadlock = [this]() {
+ // destruction from within callback detected
+ std::cerr << "SOCom error: A callback causes the Client_connector instance to be destroyed "
+ "by which the callback is called. This leads to a deadlock because the "
+ "destructor waits until all callbacks are done.: interface="
+ << m_configuration.interface.id << std::endl;
+ };
+
+ m_deadlock_detector.check_deadlock(log_on_deadlock);
+#endif
+ auto const wait_for_stop_complete = [this]() { m_stop_complete_promise.get_future().wait(); };
+ Final_action const catch_promise_exceptions{wait_for_stop_complete};
+}
+
+message::Subscribe_event::Return_type Impl::subscribe_event(Event_id client_id,
+ Event_mode mode) const noexcept {
+ return send(message::Subscribe_event{client_id, mode});
+}
+
+message::Unsubscribe_event::Return_type Impl::unsubscribe_event(Event_id client_id) const noexcept {
+ return send(message::Unsubscribe_event{client_id});
+}
+
+message::Request_event_update::Return_type Impl::request_event_update(
+ Event_id client_id) const noexcept {
+ return send(message::Request_event_update{client_id});
+}
+
+message::Call_method::Return_type Impl::call_method(
+ Method_id client_id, Payload::Sptr payload,
+ Method_call_reply_data_opt reply_data) const noexcept {
+ Method_call_reply_data_opt internal_reply_data;
+
+ if (reply_data) {
+ struct Context {
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ Impl const* self;
+#endif
+ Method_reply_callback reply_callback;
+ Weak_reference_token weak_stop_block_token;
+
+ Context(
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ Impl const* self,
+#endif
+ Method_reply_callback reply_callback, Weak_reference_token weak_stop_block_token)
+ :
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ self{self},
+#endif
+ reply_callback{std::move(reply_callback)},
+ weak_stop_block_token{std::move(weak_stop_block_token)} {
+ }
+ };
+
+ auto context = std::make_unique(
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ this,
+#endif
+ std::move(reply_data->reply_callback), create_weak_block_token());
+
+ auto wrapped_reply_callback = [context =
+ std::move(context)](Method_result const& method_reply) {
+ auto const stop_block_token = context->weak_stop_block_token.lock();
+ if (stop_block_token) {
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ Temporary_thread_id_add const tmptia{
+ context->self->m_deadlock_detector.enter_callback()};
+#endif
+ (context->reply_callback)(method_reply);
+ }
+ };
+
+ internal_reply_data.emplace(Method_call_reply_data{std::move(wrapped_reply_callback),
+ std::move(reply_data->reply_payload)});
+ }
+
+ return send(
+ message::Call_method{client_id, payload, std::move(internal_reply_data), m_credentials});
+}
+
+Result> Impl::allocate_method_call_payload(
+ Method_id method_id) noexcept {
+ return send(message::Allocate_method_call_payload{method_id});
+}
+
+Result Impl::get_peer_credentials() const noexcept {
+ return send(message::Posix_credentials{});
+}
+
+Service_interface_definition const& Impl::get_configuration() const noexcept {
+ return m_configuration;
+}
+
+Service_instance const& Impl::get_service_instance() const noexcept { return m_instance; }
+
+bool Impl::is_service_available() const noexcept { return m_server.has_value(); }
+
+message::Service_state_change::Return_type Impl::receive(message::Service_state_change message) {
+ if (message.state == Service_state::not_available) {
+ std::lock_guard const lock{m_mutex};
+ m_server.reset();
+ }
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ Temporary_thread_id_add const tmptia{m_deadlock_detector.enter_callback()};
+#endif
+ m_callbacks.on_service_state_change(*this, message.state, message.configuration);
+}
+
+message::Update_event::Return_type Impl::receive(message::Update_event message) {
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ Temporary_thread_id_add const tmptia{m_deadlock_detector.enter_callback()};
+#endif
+ m_callbacks.on_event_update(*this, message.id, message.payload);
+}
+
+message::Update_requested_event::Return_type Impl::receive(
+ message::Update_requested_event message) {
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ Temporary_thread_id_add const tmptia{m_deadlock_detector.enter_callback()};
+#endif
+ m_callbacks.on_event_requested_update(*this, message.id, message.payload);
+}
+
+message::Allocate_event_payload::Return_type Impl::receive(
+ message::Allocate_event_payload message) {
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ Temporary_thread_id_add const tmptia{m_deadlock_detector.enter_callback()};
+#endif
+ return m_callbacks.on_event_payload_allocate(*this, message.id);
+}
+
+Impl::Server_indication Impl::make_on_server_update_callback() {
+ return [this, weak_stop_token = create_weak_block_token()](
+ Server_connector_listen_endpoint const& listen_endpoint) {
+ auto const locked_token = weak_stop_token.lock();
+ // Destroying client-connector before this callback runs is not possible with
+ // deterministic results.
+
+ if (nullptr == locked_token) {
+ // Client_connector destruction detected
+ return;
+ }
+
+ auto endpoint = Client_connector_endpoint(*this, locked_token);
+ auto const connect_return = listen_endpoint.send(message::Connect{endpoint});
+ // Endpoint not accessible for testing to inject determinstic error-condition
+
+ if (!connect_return) {
+ return;
+ }
+
+ // As the false condition happens on the non deterministic behavior of thread scheduling
+ // it cannot be tested reliably in unit tests and is therefore excluded in the coverage.
+
+ if (set_id_mappings_and_server(*connect_return)) {
+ receive(connect_return->service_state);
+ }
+ };
+}
+
+bool Impl::set_id_mappings_and_server(message::Connect_return const& connect_return) {
+ std::lock_guard const lock{m_mutex};
+ // The dtor could have been started by another thread while this callback is active.
+ // If the dtor is active setting m_server will lead to a deadlock. Thus check if the
+ // dtor is running by checking m_stop_block_token for nullptr.
+ // As this happens on the non deterministic behavior of thread scheduling it cannot
+ // be tested reliably in unit tests and is therefore excluded in the coverage.
+ // Only Bullseye is excluded as it may happen that the following condition is never
+ // false.
+
+ if (nullptr == m_stop_block_token) {
+ return false;
+ }
+
+ m_server = connect_return.endpoint;
+ return true;
+}
+
+Weak_reference_token Impl::create_weak_block_token() const {
+ std::lock_guard const lock{m_mutex};
+ return Weak_reference_token{m_stop_block_token};
+}
+
+} // namespace client_connector
+
+} // namespace socom
+} // namespace score
diff --git a/src/socom/src/client_connector_impl.hpp b/src/socom/src/client_connector_impl.hpp
new file mode 100644
index 00000000..5dabd07e
--- /dev/null
+++ b/src/socom/src/client_connector_impl.hpp
@@ -0,0 +1,127 @@
+/********************************************************************************
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+#ifndef SCORE_SOCOM_CLIENT_CONNECTOR_IMPL_HPP
+#define SCORE_SOCOM_CLIENT_CONNECTOR_IMPL_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "endpoint.hpp"
+#include "messages.hpp"
+#include "runtime_registration.hpp"
+#include "temporary_thread_id_add.hpp"
+
+namespace score {
+namespace socom {
+
+class Runtime_impl;
+
+namespace client_connector {
+// deadlock detection.
+class Impl final : public Client_connector {
+ public:
+ using Endpoint = Client_connector_endpoint;
+
+ using Server_indication =
+ std::function;
+
+ Impl(Runtime_impl& runtime, Service_interface_definition configuration,
+ Service_instance instance, Client_connector::Callbacks callbacks,
+ Posix_credentials const& credentials);
+ Impl(Impl const&) = delete;
+ Impl(Impl&&) = delete;
+ Impl& operator=(Impl const&) = delete;
+ Impl& operator=(Impl&&) = delete;
+
+ ~Impl() noexcept override;
+
+ // interface ::score::socom::Client_connector
+ Result> allocate_method_call_payload(
+ Method_id method_id) noexcept override;
+ message::Subscribe_event::Return_type subscribe_event(Event_id client_id,
+ Event_mode mode) const noexcept override;
+ message::Unsubscribe_event::Return_type unsubscribe_event(
+ Event_id client_id) const noexcept override;
+ message::Request_event_update::Return_type request_event_update(
+ Event_id client_id) const noexcept override;
+ message::Call_method::Return_type call_method(
+ Method_id client_id, Payload::Sptr payload,
+ Method_call_reply_data_opt reply_data) const noexcept override;
+ Result get_peer_credentials() const noexcept override;
+ Service_interface_definition const& get_configuration() const noexcept override;
+ Service_instance const& get_service_instance() const noexcept override;
+ bool is_service_available() const noexcept override;
+
+ // Endpoint API
+ message::Service_state_change::Return_type receive(message::Service_state_change message);
+ message::Update_event::Return_type receive(message::Update_event message);
+ message::Update_requested_event::Return_type receive(message::Update_requested_event message);
+ message::Allocate_event_payload::Return_type receive(message::Allocate_event_payload message);
+
+ private:
+ template
+ ReturnType lock_server(F const& on_server_locked) const;
+ Server_indication make_on_server_update_callback();
+
+ bool set_id_mappings_and_server(message::Connect_return const& connect_return);
+
+ Weak_reference_token create_weak_block_token() const;
+
+ // Endpoint APIs
+ template
+ typename MessageType::Return_type send(MessageType message) const;
+
+ Service_interface_definition const m_configuration;
+ Service_instance const m_instance;
+ Client_connector::Callbacks const m_callbacks;
+#ifdef WITH_SOCOM_DEADLOCK_DETECTION
+ mutable Deadlock_detector m_deadlock_detector;
+#endif
+ mutable std::mutex m_mutex;
+ std::promise m_stop_complete_promise;
+ Reference_token m_stop_block_token; // Protected by m_mutex
+ std::optional m_server; // Protected by m_mutex
+ Registration m_registration; // Protected by m_mutex
+ Posix_credentials m_credentials;
+};
+
+template
+ReturnType Impl::lock_server(F const& on_server_locked) const {
+ std::unique_lock lock{m_mutex};
+ auto const locked_server = m_server;
+ lock.unlock();
+
+ if (locked_server) {
+ return on_server_locked(*locked_server);
+ }
+ return MakeUnexpected(Error::runtime_error_service_not_available);
+}
+
+template