From d88187b13eb9322d575ef5af8b9c80949898eb51 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Wed, 24 Jul 2024 21:33:16 +0200 Subject: [PATCH 01/35] [#210] Add publisher and subscriber builder --- iceoryx2-ffi/ffi/src/api/mod.rs | 4 + .../ffi/src/api/port_factory_pub_sub.rs | 115 +++++++++++++++++- .../src/api/port_factory_publisher_builder.rs | 114 +++++++++++++++++ .../api/port_factory_subscriber_builder.rs | 114 +++++++++++++++++ 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs create mode 100644 iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs diff --git a/iceoryx2-ffi/ffi/src/api/mod.rs b/iceoryx2-ffi/ffi/src/api/mod.rs index b458338ca..55b41a33d 100644 --- a/iceoryx2-ffi/ffi/src/api/mod.rs +++ b/iceoryx2-ffi/ffi/src/api/mod.rs @@ -29,6 +29,8 @@ mod port_factory_event; mod port_factory_listener_builder; mod port_factory_notifier_builder; mod port_factory_pub_sub; +mod port_factory_publisher_builder; +mod port_factory_subscriber_builder; mod publisher; mod quirks_correction; mod service; @@ -50,6 +52,8 @@ pub use port_factory_event::*; pub use port_factory_listener_builder::*; pub use port_factory_notifier_builder::*; pub use port_factory_pub_sub::*; +pub use port_factory_publisher_builder::*; +pub use port_factory_subscriber_builder::*; pub use publisher::*; pub use quirks_correction::*; pub use service::*; diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs index 4688162cf..dabf5c247 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs @@ -12,7 +12,12 @@ #![allow(non_camel_case_types)] -use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::api::{ + iox2_port_factory_publisher_builder_h, iox2_port_factory_publisher_builder_t, + iox2_port_factory_subscriber_builder_h, iox2_port_factory_subscriber_builder_t, + iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi, + PortFactoryPublisherBuilderUnion, PortFactorySubscriberBuilderUnion, +}; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::publish_subscribe::PortFactory; @@ -122,6 +127,114 @@ pub unsafe extern "C" fn iox2_cast_port_factory_pub_sub_ref_h( (*port_factory_handle.as_type()).as_ref_handle() as *mut _ as _ } +/// Instantiates a [`iox2_port_factory_publisher_builder_h`] to build a publisher. +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_pub_sub_ref_h`] obtained +/// by e.g. [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create) +/// and casted by [`iox2_cast_port_factory_pub_sub_ref_h`] +/// * `publisher_builder_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_port_factory_publisher_builder_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// +/// Returns the `iox2_port_factory_publisher_builder_h` handle for the publisher builder. +/// +/// # Safety +/// +/// * The `port_factory_handle` is still valid after the return of this function and can be use in another function call. +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_publisher_builder( + port_factory_handle: iox2_port_factory_pub_sub_ref_h, + publisher_builder_struct_ptr: *mut iox2_port_factory_publisher_builder_t, +) -> iox2_port_factory_publisher_builder_h { + debug_assert!(!port_factory_handle.is_null()); + + let mut publisher_builder_struct_ptr = publisher_builder_struct_ptr; + fn no_op(_: *mut iox2_port_factory_publisher_builder_t) {} + let mut deleter: fn(*mut iox2_port_factory_publisher_builder_t) = no_op; + if publisher_builder_struct_ptr.is_null() { + publisher_builder_struct_ptr = iox2_port_factory_publisher_builder_t::alloc(); + deleter = iox2_port_factory_publisher_builder_t::dealloc; + } + debug_assert!(!publisher_builder_struct_ptr.is_null()); + + let port_factory = &mut *port_factory_handle.as_type(); + match port_factory.service_type { + iox2_service_type_e::IPC => { + let publisher_builder = port_factory.value.as_ref().ipc.publisher_builder(); + (*publisher_builder_struct_ptr).init( + port_factory.service_type, + PortFactoryPublisherBuilderUnion::new_ipc(publisher_builder), + deleter, + ); + } + iox2_service_type_e::LOCAL => { + let publisher_builder = port_factory.value.as_ref().local.publisher_builder(); + (*publisher_builder_struct_ptr).init( + port_factory.service_type, + PortFactoryPublisherBuilderUnion::new_local(publisher_builder), + deleter, + ); + } + }; + + (*publisher_builder_struct_ptr).as_handle() +} + +/// Instantiates a [`iox2_port_factory_subscriber_builder_h`] to build a subscriber. +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_pub_sub_ref_h`] obtained +/// by e.g. [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create) +/// and casted by [`iox2_cast_port_factory_pub_sub_ref_h`] +/// * `subscriber_builder_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_port_factory_subscriber_builder_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// +/// Returns the [`iox2_port_factory_subscriber_builder_h`] handle for the subscriber builder. +/// +/// # Safety +/// +/// * The `port_factory_handle` is still valid after the return of this function and can be use in another function call. +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_subscriber_builder( + port_factory_handle: iox2_port_factory_pub_sub_ref_h, + subscriber_builder_struct_ptr: *mut iox2_port_factory_subscriber_builder_t, +) -> iox2_port_factory_subscriber_builder_h { + debug_assert!(!port_factory_handle.is_null()); + + let mut subscriber_builder_struct_ptr = subscriber_builder_struct_ptr; + fn no_op(_: *mut iox2_port_factory_subscriber_builder_t) {} + let mut deleter: fn(*mut iox2_port_factory_subscriber_builder_t) = no_op; + if subscriber_builder_struct_ptr.is_null() { + subscriber_builder_struct_ptr = iox2_port_factory_subscriber_builder_t::alloc(); + deleter = iox2_port_factory_subscriber_builder_t::dealloc; + } + debug_assert!(!subscriber_builder_struct_ptr.is_null()); + + let port_factory = &mut *port_factory_handle.as_type(); + match port_factory.service_type { + iox2_service_type_e::IPC => { + let subscriber_builder = port_factory.value.as_ref().ipc.subscriber_builder(); + (*subscriber_builder_struct_ptr).init( + port_factory.service_type, + PortFactorySubscriberBuilderUnion::new_ipc(subscriber_builder), + deleter, + ); + } + iox2_service_type_e::LOCAL => { + let subscriber_builder = port_factory.value.as_ref().local.subscriber_builder(); + (*subscriber_builder_struct_ptr).init( + port_factory.service_type, + PortFactorySubscriberBuilderUnion::new_local(subscriber_builder), + deleter, + ); + } + }; + + (*subscriber_builder_struct_ptr).as_handle() +} + /// This function needs to be called to destroy the port factory! /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs new file mode 100644 index 000000000..e7ae1ca2a --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; + +use iceoryx2::prelude::*; +use iceoryx2::service::port_factory::publisher::PortFactoryPublisher; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::mem::ManuallyDrop; + +// BEGIN types definition + +pub(super) union PortFactoryPublisherBuilderUnion { + ipc: ManuallyDrop< + PortFactoryPublisher<'static, zero_copy::Service, PayloadFfi, NoUserHeaderFfi>, + >, + local: ManuallyDrop< + PortFactoryPublisher<'static, process_local::Service, PayloadFfi, NoUserHeaderFfi>, + >, +} + +impl PortFactoryPublisherBuilderUnion { + pub(super) fn new_ipc( + port_factory: PortFactoryPublisher< + 'static, + zero_copy::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + ipc: ManuallyDrop::new(port_factory), + } + } + pub(super) fn new_local( + port_factory: PortFactoryPublisher< + 'static, + process_local::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + local: ManuallyDrop::new(port_factory), + } + } +} + +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_port_factory_publisher_builder_storage_t { + internal: [u8; 128], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(PortFactoryPublisherBuilderUnion)] +pub struct iox2_port_factory_publisher_builder_t { + service_type: iox2_service_type_e, + value: iox2_port_factory_publisher_builder_storage_t, + deleter: fn(*mut iox2_port_factory_publisher_builder_t), +} + +impl iox2_port_factory_publisher_builder_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: PortFactoryPublisherBuilderUnion, + deleter: fn(*mut iox2_port_factory_publisher_builder_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_port_factory_publisher_builder_h_t; +/// The owning handle for `iox2_port_factory_publisher_builder_t`. Passing the handle to an function transfers the ownership. +pub type iox2_port_factory_publisher_builder_h = *mut iox2_port_factory_publisher_builder_h_t; + +pub struct iox2_port_factory_publisher_builder_ref_h_t; +/// The non-owning handle for `iox2_port_factory_publisher_builder_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_port_factory_publisher_builder_ref_h = + *mut iox2_port_factory_publisher_builder_ref_h_t; + +impl HandleToType for iox2_port_factory_publisher_builder_h { + type Target = *mut iox2_port_factory_publisher_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_port_factory_publisher_builder_ref_h { + type Target = *mut iox2_port_factory_publisher_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs new file mode 100644 index 000000000..b340e140a --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; + +use iceoryx2::prelude::*; +use iceoryx2::service::port_factory::subscriber::PortFactorySubscriber; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::mem::ManuallyDrop; + +// BEGIN types definition + +pub(super) union PortFactorySubscriberBuilderUnion { + ipc: ManuallyDrop< + PortFactorySubscriber<'static, zero_copy::Service, PayloadFfi, NoUserHeaderFfi>, + >, + local: ManuallyDrop< + PortFactorySubscriber<'static, process_local::Service, PayloadFfi, NoUserHeaderFfi>, + >, +} + +impl PortFactorySubscriberBuilderUnion { + pub(super) fn new_ipc( + port_factory: PortFactorySubscriber< + 'static, + zero_copy::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + ipc: ManuallyDrop::new(port_factory), + } + } + pub(super) fn new_local( + port_factory: PortFactorySubscriber< + 'static, + process_local::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + local: ManuallyDrop::new(port_factory), + } + } +} + +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_port_factory_subscriber_builder_storage_t { + internal: [u8; 112], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(PortFactorySubscriberBuilderUnion)] +pub struct iox2_port_factory_subscriber_builder_t { + service_type: iox2_service_type_e, + value: iox2_port_factory_subscriber_builder_storage_t, + deleter: fn(*mut iox2_port_factory_subscriber_builder_t), +} + +impl iox2_port_factory_subscriber_builder_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: PortFactorySubscriberBuilderUnion, + deleter: fn(*mut iox2_port_factory_subscriber_builder_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_port_factory_subscriber_builder_h_t; +/// The owning handle for `iox2_port_factory_subscriber_builder_t`. Passing the handle to an function transfers the ownership. +pub type iox2_port_factory_subscriber_builder_h = *mut iox2_port_factory_subscriber_builder_h_t; + +pub struct iox2_port_factory_subscriber_builder_ref_h_t; +/// The non-owning handle for `iox2_port_factory_subscriber_builder_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_port_factory_subscriber_builder_ref_h = + *mut iox2_port_factory_subscriber_builder_ref_h_t; + +impl HandleToType for iox2_port_factory_subscriber_builder_h { + type Target = *mut iox2_port_factory_subscriber_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_port_factory_subscriber_builder_ref_h { + type Target = *mut iox2_port_factory_subscriber_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition From 6552285ec91072005779f2ee3cd6ebd6c1b86167 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Wed, 24 Jul 2024 23:05:09 +0200 Subject: [PATCH 02/35] [#210] Create subscriber --- .../api/port_factory_subscriber_builder.rs | 170 +++++++++++++++++- iceoryx2-ffi/ffi/src/api/subscriber.rs | 121 ++++++++++++- 2 files changed, 289 insertions(+), 2 deletions(-) diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs index b340e140a..5b80f5cb6 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs @@ -12,17 +12,42 @@ #![allow(non_camel_case_types)] -use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::api::{ + c_size_t, iox2_service_type_e, iox2_subscriber_h, iox2_subscriber_t, HandleToType, IntoCInt, + NoUserHeaderFfi, PayloadFfi, SubscriberUnion, IOX2_OK, +}; +use iceoryx2::port::subscriber::SubscriberCreateError; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::subscriber::PortFactorySubscriber; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; +use core::ffi::c_int; use core::mem::ManuallyDrop; // BEGIN types definition +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_subscriber_create_error_e { + EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS = IOX2_OK as isize + 1, + BUFFER_SIZE_EXCEEDS_MAX_SUPPORTED_BUFFER_SIZE_OF_SERVICE, +} + +impl IntoCInt for SubscriberCreateError { + fn into_c_int(self) -> c_int { + (match self { + SubscriberCreateError::ExceedsMaxSupportedSubscribers => { + iox2_subscriber_create_error_e::EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS + } + SubscriberCreateError::BufferSizeExceedsMaxSupportedBufferSizeOfService => { + iox2_subscriber_create_error_e::BUFFER_SIZE_EXCEEDS_MAX_SUPPORTED_BUFFER_SIZE_OF_SERVICE + } + }) as c_int + } +} + pub(super) union PortFactorySubscriberBuilderUnion { ipc: ManuallyDrop< PortFactorySubscriber<'static, zero_copy::Service, PayloadFfi, NoUserHeaderFfi>, @@ -112,3 +137,146 @@ impl HandleToType for iox2_port_factory_subscriber_builder_ref_h { } // END type definition + +// BEGIN C API + +/// This function casts an owning [`iox2_port_factory_subscriber_builder_h`] into a non-owning [`iox2_port_factory_subscriber_builder_ref_h`] +/// +/// # Arguments +/// +/// * `port_factory_handle` obtained by [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder) +/// +/// Returns a [`iox2_port_factory_subscriber_builder_ref_h`] +/// +/// # Safety +/// +/// * The `port_factory_handle` must be a valid handle. +/// * The `port_factory_handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_port_factory_subscriber_builder_ref_h( + port_factory_handle: iox2_port_factory_subscriber_builder_h, +) -> iox2_port_factory_subscriber_builder_ref_h { + debug_assert!(!port_factory_handle.is_null()); + + (*port_factory_handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Sets the buffer size for the subscriber +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_subscriber_builder_ref_h`] +/// obtained by [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder) and +/// casted by [`iox2_cast_port_factory_subscriber_builder_ref_h`]. +/// * `value` - The value to set buffer size to +/// +/// # Safety +/// +/// * `port_factory_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_subscriber_builder_set_buffer_size( + port_factory_handle: iox2_port_factory_subscriber_builder_ref_h, + value: c_size_t, +) { + debug_assert!(!port_factory_handle.is_null()); + + let port_factory_struct = unsafe { &mut *port_factory_handle.as_type() }; + match port_factory_struct.service_type { + iox2_service_type_e::IPC => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().ipc); + + port_factory_struct.set(PortFactorySubscriberBuilderUnion::new_ipc( + port_factory.buffer_size(value), + )); + } + iox2_service_type_e::LOCAL => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().local); + + port_factory_struct.set(PortFactorySubscriberBuilderUnion::new_local( + port_factory.buffer_size(value), + )); + } + } +} + +// TODO [#210] add all the other setter methods + +/// Creates a subscriber and consumes the builder +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_subscriber_builder_h`] obtained by [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder). +/// * `subscriber_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_subscriber_t`]. If it is a NULL pointer, the storage will be allocated on the heap. +/// * `subscriber_handle_ptr` - An uninitialized or dangling [`iox2_subscriber_h`] handle which will be initialized by this function call. +/// +/// Returns IOX2_OK on success, an [`iox2_subscriber_create_error_e`] otherwise. +/// +/// # Safety +/// +/// * The `port_factory_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_port_factory_subscriber_builder_t`] +/// can be re-used with a call to [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_subscriber_builder_create( + port_factory_handle: iox2_port_factory_subscriber_builder_h, + subscriber_struct_ptr: *mut iox2_subscriber_t, + subscriber_handle_ptr: *mut iox2_subscriber_h, +) -> c_int { + debug_assert!(!port_factory_handle.is_null()); + debug_assert!(!subscriber_handle_ptr.is_null()); + + let mut subscriber_struct_ptr = subscriber_struct_ptr; + fn no_op(_: *mut iox2_subscriber_t) {} + let mut deleter: fn(*mut iox2_subscriber_t) = no_op; + if subscriber_struct_ptr.is_null() { + subscriber_struct_ptr = iox2_subscriber_t::alloc(); + deleter = iox2_subscriber_t::dealloc; + } + debug_assert!(!subscriber_struct_ptr.is_null()); + + let subscriber_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; + + let service_type = subscriber_builder_struct.service_type; + match service_type { + iox2_service_type_e::IPC => { + let subscriber_builder = + ManuallyDrop::take(&mut subscriber_builder_struct.value.as_mut().ipc); + + match subscriber_builder.create() { + Ok(subscriber) => { + (*subscriber_struct_ptr).init( + service_type, + SubscriberUnion::new_ipc(subscriber), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + iox2_service_type_e::LOCAL => { + let subscriber_builder = + ManuallyDrop::take(&mut subscriber_builder_struct.value.as_mut().local); + + match subscriber_builder.create() { + Ok(subscriber) => { + (*subscriber_struct_ptr).init( + service_type, + SubscriberUnion::new_local(subscriber), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + } + + *subscriber_handle_ptr = (*subscriber_struct_ptr).as_handle(); + + IOX2_OK +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/subscriber.rs b/iceoryx2-ffi/ffi/src/api/subscriber.rs index 06b38101a..b9c6af151 100644 --- a/iceoryx2-ffi/ffi/src/api/subscriber.rs +++ b/iceoryx2-ffi/ffi/src/api/subscriber.rs @@ -10,8 +10,127 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use core::time::Duration; +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; + +use iceoryx2::port::subscriber::Subscriber; use iceoryx2::prelude::*; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::mem::ManuallyDrop; + +// BEGIN types definition + +pub(super) union SubscriberUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl SubscriberUnion { + pub(super) fn new_ipc( + subscriber: Subscriber, + ) -> Self { + Self { + ipc: ManuallyDrop::new(subscriber), + } + } + pub(super) fn new_local( + subscriber: Subscriber, + ) -> Self { + Self { + local: ManuallyDrop::new(subscriber), + } + } +} + +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_subscriber_storage_t { + internal: [u8; 448], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(SubscriberUnion)] +pub struct iox2_subscriber_t { + service_type: iox2_service_type_e, + value: iox2_subscriber_storage_t, + deleter: fn(*mut iox2_subscriber_t), +} + +impl iox2_subscriber_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: SubscriberUnion, + deleter: fn(*mut iox2_subscriber_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_subscriber_h_t; +/// The owning handle for `iox2_subscriber_t`. Passing the handle to an function transfers the ownership. +pub type iox2_subscriber_h = *mut iox2_subscriber_h_t; + +pub struct iox2_subscriber_ref_h_t; +/// The non-owning handle for `iox2_subscriber_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_subscriber_ref_h = *mut iox2_subscriber_ref_h_t; + +impl HandleToType for iox2_subscriber_h { + type Target = *mut iox2_subscriber_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_subscriber_ref_h { + type Target = *mut iox2_subscriber_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +/// This function needs to be called to destroy the subscriber! +/// +/// # Arguments +/// +/// * `subscriber_handle` - A valid [`iox2_subscriber_h`] +/// +/// # Safety +/// +/// * The `subscriber_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_subscriber_t`] can be re-used with a call to +/// [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_subscriber_drop(subscriber_handle: iox2_subscriber_h) { + debug_assert!(!subscriber_handle.is_null()); + + let subscriber = &mut *subscriber_handle.as_type(); + + match subscriber.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut subscriber.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut subscriber.value.as_mut().local); + } + } + (subscriber.deleter)(subscriber); +} + +// END C API + +use core::time::Duration; use iceoryx2_bb_log::set_log_level; const CYCLE_TIME: Duration = Duration::from_secs(1); From 4a9abf1050764584be74af967268ebee74c59dee Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Wed, 24 Jul 2024 23:20:20 +0200 Subject: [PATCH 03/35] [#210] Create publisher --- .../src/api/port_factory_publisher_builder.rs | 170 +++++++++++++++++- iceoryx2-ffi/ffi/src/api/publisher.rs | 121 ++++++++++++- 2 files changed, 289 insertions(+), 2 deletions(-) diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs index e7ae1ca2a..aafc8f2b4 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs @@ -12,17 +12,42 @@ #![allow(non_camel_case_types)] -use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::api::{ + c_size_t, iox2_publisher_h, iox2_publisher_t, iox2_service_type_e, HandleToType, IntoCInt, + NoUserHeaderFfi, PayloadFfi, PublisherUnion, IOX2_OK, +}; +use iceoryx2::port::publisher::PublisherCreateError; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::publisher::PortFactoryPublisher; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; +use core::ffi::c_int; use core::mem::ManuallyDrop; // BEGIN types definition +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_publisher_create_error_e { + EXCEEDS_MAX_SUPPORTED_PUBLISHERS = IOX2_OK as isize + 1, + UNABLE_TO_CREAT_DATA_SEGMENT, +} + +impl IntoCInt for PublisherCreateError { + fn into_c_int(self) -> c_int { + (match self { + PublisherCreateError::ExceedsMaxSupportedPublishers => { + iox2_publisher_create_error_e::EXCEEDS_MAX_SUPPORTED_PUBLISHERS + } + PublisherCreateError::UnableToCreateDataSegment => { + iox2_publisher_create_error_e::UNABLE_TO_CREAT_DATA_SEGMENT + } + }) as c_int + } +} + pub(super) union PortFactoryPublisherBuilderUnion { ipc: ManuallyDrop< PortFactoryPublisher<'static, zero_copy::Service, PayloadFfi, NoUserHeaderFfi>, @@ -112,3 +137,146 @@ impl HandleToType for iox2_port_factory_publisher_builder_ref_h { } // END type definition + +// BEGIN C API + +/// This function casts an owning [`iox2_port_factory_publisher_builder_h`] into a non-owning [`iox2_port_factory_publisher_builder_ref_h`] +/// +/// # Arguments +/// +/// * `port_factory_handle` obtained by [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder) +/// +/// Returns a [`iox2_port_factory_publisher_builder_ref_h`] +/// +/// # Safety +/// +/// * The `port_factory_handle` must be a valid handle. +/// * The `port_factory_handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_port_factory_publisher_builder_ref_h( + port_factory_handle: iox2_port_factory_publisher_builder_h, +) -> iox2_port_factory_publisher_builder_ref_h { + debug_assert!(!port_factory_handle.is_null()); + + (*port_factory_handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Sets the max loaned samples for the publisher +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_publisher_builder_ref_h`] +/// obtained by [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder) and +/// casted by [`iox2_cast_port_factory_publisher_builder_ref_h`]. +/// * `value` - The value to set max loaned samples to +/// +/// # Safety +/// +/// * `port_factory_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_publisher_builder_set_max_loaned_samples( + port_factory_handle: iox2_port_factory_publisher_builder_ref_h, + value: c_size_t, +) { + debug_assert!(!port_factory_handle.is_null()); + + let port_factory_struct = unsafe { &mut *port_factory_handle.as_type() }; + match port_factory_struct.service_type { + iox2_service_type_e::IPC => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().ipc); + + port_factory_struct.set(PortFactoryPublisherBuilderUnion::new_ipc( + port_factory.max_loaned_samples(value), + )); + } + iox2_service_type_e::LOCAL => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().local); + + port_factory_struct.set(PortFactoryPublisherBuilderUnion::new_local( + port_factory.max_loaned_samples(value), + )); + } + } +} + +// TODO [#210] add all the other setter methods + +/// Creates a publisher and consumes the builder +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_publisher_builder_h`] obtained by [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder). +/// * `publisher_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_publisher_t`]. If it is a NULL pointer, the storage will be allocated on the heap. +/// * `publisher_handle_ptr` - An uninitialized or dangling [`iox2_publisher_h`] handle which will be initialized by this function call. +/// +/// Returns IOX2_OK on success, an [`iox2_publisher_create_error_e`] otherwise. +/// +/// # Safety +/// +/// * The `port_factory_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_port_factory_publisher_builder_t`] +/// can be re-used with a call to [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_publisher_builder_create( + port_factory_handle: iox2_port_factory_publisher_builder_h, + publisher_struct_ptr: *mut iox2_publisher_t, + publisher_handle_ptr: *mut iox2_publisher_h, +) -> c_int { + debug_assert!(!port_factory_handle.is_null()); + debug_assert!(!publisher_handle_ptr.is_null()); + + let mut publisher_struct_ptr = publisher_struct_ptr; + fn no_op(_: *mut iox2_publisher_t) {} + let mut deleter: fn(*mut iox2_publisher_t) = no_op; + if publisher_struct_ptr.is_null() { + publisher_struct_ptr = iox2_publisher_t::alloc(); + deleter = iox2_publisher_t::dealloc; + } + debug_assert!(!publisher_struct_ptr.is_null()); + + let publisher_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; + + let service_type = publisher_builder_struct.service_type; + match service_type { + iox2_service_type_e::IPC => { + let publisher_builder = + ManuallyDrop::take(&mut publisher_builder_struct.value.as_mut().ipc); + + match publisher_builder.create() { + Ok(publisher) => { + (*publisher_struct_ptr).init( + service_type, + PublisherUnion::new_ipc(publisher), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + iox2_service_type_e::LOCAL => { + let publisher_builder = + ManuallyDrop::take(&mut publisher_builder_struct.value.as_mut().local); + + match publisher_builder.create() { + Ok(publisher) => { + (*publisher_struct_ptr).init( + service_type, + PublisherUnion::new_local(publisher), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + } + + *publisher_handle_ptr = (*publisher_struct_ptr).as_handle(); + + IOX2_OK +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/publisher.rs b/iceoryx2-ffi/ffi/src/api/publisher.rs index 3dccb8ed4..dab15dd04 100644 --- a/iceoryx2-ffi/ffi/src/api/publisher.rs +++ b/iceoryx2-ffi/ffi/src/api/publisher.rs @@ -10,8 +10,127 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use core::time::Duration; +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; + +use iceoryx2::port::publisher::Publisher; use iceoryx2::prelude::*; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::mem::ManuallyDrop; + +// BEGIN types definition + +pub(super) union PublisherUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl PublisherUnion { + pub(super) fn new_ipc( + publisher: Publisher, + ) -> Self { + Self { + ipc: ManuallyDrop::new(publisher), + } + } + pub(super) fn new_local( + publisher: Publisher, + ) -> Self { + Self { + local: ManuallyDrop::new(publisher), + } + } +} + +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_publisher_storage_t { + internal: [u8; 40], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(PublisherUnion)] +pub struct iox2_publisher_t { + service_type: iox2_service_type_e, + value: iox2_publisher_storage_t, + deleter: fn(*mut iox2_publisher_t), +} + +impl iox2_publisher_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: PublisherUnion, + deleter: fn(*mut iox2_publisher_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_publisher_h_t; +/// The owning handle for `iox2_publisher_t`. Passing the handle to an function transfers the ownership. +pub type iox2_publisher_h = *mut iox2_publisher_h_t; + +pub struct iox2_publisher_ref_h_t; +/// The non-owning handle for `iox2_publisher_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_publisher_ref_h = *mut iox2_publisher_ref_h_t; + +impl HandleToType for iox2_publisher_h { + type Target = *mut iox2_publisher_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_publisher_ref_h { + type Target = *mut iox2_publisher_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +/// This function needs to be called to destroy the publisher! +/// +/// # Arguments +/// +/// * `publisher_handle` - A valid [`iox2_publisher_h`] +/// +/// # Safety +/// +/// * The `publisher_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_publisher_t`] can be re-used with a call to +/// [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_publisher_drop(publisher_handle: iox2_publisher_h) { + debug_assert!(!publisher_handle.is_null()); + + let publisher = &mut *publisher_handle.as_type(); + + match publisher.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut publisher.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut publisher.value.as_mut().local); + } + } + (publisher.deleter)(publisher); +} + +// END C API + +use core::time::Duration; use iceoryx2_bb_log::set_log_level; const CYCLE_TIME: Duration = Duration::from_secs(1); From a7907cccc4a0be81ffa45306e27c2da74ec8630b Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 11:33:39 +0200 Subject: [PATCH 04/35] [#264] Add port factory publisher --- .../iox2/port_factory_publish_subscribe.hpp | 4 +- .../include/iox2/port_factory_publisher.hpp | 42 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp index a6ce390af..1e3031366 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp @@ -162,7 +162,9 @@ inline auto PortFactoryPublishSubscribe::subscriber_buil template inline auto PortFactoryPublishSubscribe::publisher_builder() const -> PortFactoryPublisher { - IOX_TODO(); + auto* ref_handle = iox2_cast_port_factory_pub_sub_ref_h(m_handle); + return PortFactoryPublisher( + iox2_port_factory_pub_sub_publisher_builder(ref_handle, nullptr)); } diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp index 25b20c29d..4bf08fd23 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp @@ -16,8 +16,9 @@ #include "iox/assertions_addendum.hpp" #include "iox/builder_addendum.hpp" #include "iox/expected.hpp" -#include "publisher.hpp" -#include "service_type.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/publisher.hpp" +#include "iox2/service_type.hpp" #include @@ -34,10 +35,41 @@ class PortFactoryPublisher { IOX_BUILDER_OPTIONAL(uint64_t, max_slice_len); public: - auto create() && -> iox::expected, PublisherCreateError> { - IOX_TODO(); - } + PortFactoryPublisher(const PortFactoryPublisher&) = delete; + PortFactoryPublisher(PortFactoryPublisher&&) = default; + auto operator=(const PortFactoryPublisher&) -> PortFactoryPublisher& = delete; + auto operator=(PortFactoryPublisher&&) -> PortFactoryPublisher& = default; + ~PortFactoryPublisher() = default; + + auto create() && -> iox::expected, PublisherCreateError>; + + private: + template + friend class PortFactoryPublishSubscribe; + + explicit PortFactoryPublisher(iox2_port_factory_publisher_builder_h handle); + + iox2_port_factory_publisher_builder_h m_handle; }; + +template +inline PortFactoryPublisher::PortFactoryPublisher(iox2_port_factory_publisher_builder_h handle) + : m_handle { handle } { +} + +template +inline auto +PortFactoryPublisher::create() && -> iox::expected, + PublisherCreateError> { + auto* ref_handle = iox2_cast_port_factory_publisher_builder_ref_h(m_handle); + + m_unable_to_deliver_strategy.and_then([](auto) { IOX_TODO(); }); + m_max_slice_len.and_then([](auto) { IOX_TODO(); }); + m_max_loaned_samples.and_then( + [&](auto value) { iox2_port_factory_publisher_builder_set_max_loaned_samples(ref_handle, value); }); + + IOX_TODO(); +} } // namespace iox2 #endif From 6fd15bb7fd09098a5356c5d3620b58893726e9cd Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 11:58:00 +0200 Subject: [PATCH 05/35] [#264] Implement port factory subscriber --- .../iox2/port_factory_publish_subscribe.hpp | 4 +- .../include/iox2/port_factory_subscriber.hpp | 42 ++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp index 1e3031366..84fc89123 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp @@ -156,7 +156,9 @@ inline auto PortFactoryPublishSubscribe::nodes( template inline auto PortFactoryPublishSubscribe::subscriber_builder() const -> PortFactorySubscriber { - IOX_TODO(); + auto* ref_handle = iox2_cast_port_factory_pub_sub_ref_h(m_handle); + return PortFactorySubscriber( + iox2_port_factory_pub_sub_subscriber_builder(ref_handle, nullptr)); } template diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp index 443dc64d9..65e3e08ab 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp @@ -16,8 +16,9 @@ #include "iox/assertions_addendum.hpp" #include "iox/builder_addendum.hpp" #include "iox/expected.hpp" -#include "service_type.hpp" -#include "subscriber.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/service_type.hpp" +#include "iox2/subscriber.hpp" #include @@ -25,13 +26,42 @@ namespace iox2 { template class PortFactorySubscriber { - IOX_BUILDER_OPTIONAL(uint64_t, history_size); + IOX_BUILDER_OPTIONAL(uint64_t, buffer_size); public: - auto create() && -> iox::expected, SubscriberCreateError> { - IOX_TODO(); - } + PortFactorySubscriber(const PortFactorySubscriber&) = delete; + PortFactorySubscriber(PortFactorySubscriber&&) = default; + auto operator=(const PortFactorySubscriber&) -> PortFactorySubscriber& = delete; + auto operator=(PortFactorySubscriber&&) -> PortFactorySubscriber& = default; + ~PortFactorySubscriber() = default; + + auto create() && -> iox::expected, SubscriberCreateError>; + + private: + template + friend class PortFactoryPublishSubscribe; + + explicit PortFactorySubscriber(iox2_port_factory_subscriber_builder_h handle); + + iox2_port_factory_subscriber_builder_h m_handle; }; + +template +inline PortFactorySubscriber::PortFactorySubscriber( + iox2_port_factory_subscriber_builder_h handle) + : m_handle { handle } { +} + +template +inline auto +PortFactorySubscriber::create() && -> iox::expected, + SubscriberCreateError> { + auto* ref_handle = iox2_cast_port_factory_subscriber_builder_ref_h(m_handle); + m_buffer_size.and_then( + [&](auto value) { iox2_port_factory_subscriber_builder_set_buffer_size(ref_handle, value); }); + + IOX_TODO(); +} } // namespace iox2 #endif From 49862abc4ab7227485f022741970e62432020c7b Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 14:17:41 +0200 Subject: [PATCH 06/35] [#264] Implement cxx publisher --- .../cxx/include/iox2/enum_translation.hpp | 12 ++ .../include/iox2/port_factory_publisher.hpp | 10 +- iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 166 +++++++++++------- .../cxx/include/iox2/publisher_error.hpp | 58 ++++++ .../src/api/port_factory_publisher_builder.rs | 4 +- iceoryx2-ffi/ffi/src/api/quirks_correction.rs | 12 +- 6 files changed, 190 insertions(+), 72 deletions(-) create mode 100644 iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index 3d9d708c7..56c7c084e 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -22,6 +22,7 @@ #include "iox2/node_event.hpp" #include "iox2/node_failure_enums.hpp" #include "iox2/notifier_error.hpp" +#include "iox2/publisher_error.hpp" #include "iox2/semantic_string.hpp" #include "iox2/service_builder_event_error.hpp" #include "iox2/service_builder_publish_subscribe_error.hpp" @@ -461,7 +462,18 @@ constexpr auto from(const int value) noexcept -> i IOX_UNREACHABLE(); } +template <> +constexpr auto from(const int value) noexcept -> iox2::PublisherCreateError { + const auto error = static_cast(value); + switch (error) { + case iox2_publisher_create_error_e_EXCEEDS_MAX_SUPPORTED_PUBLISHERS: + return iox2::PublisherCreateError::ExceedsMaxSupportedPublishers; + case iox2_publisher_create_error_e_UNABLE_TO_CREATE_DATA_SEGMENT: + return iox2::PublisherCreateError::UnableToCreateDataSegment; + } + IOX_UNREACHABLE(); +} } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp index 4bf08fd23..8e85a1dff 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp @@ -68,7 +68,15 @@ PortFactoryPublisher::create() && -> iox::expected(pub_handle)); + } + + return iox::err(iox::into(result)); } } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 1de9e65d9..c23aa0e84 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -13,92 +13,124 @@ #ifndef IOX2_PUBLISHER_HPP #define IOX2_PUBLISHER_HPP -#include "connection_failure.hpp" #include "iox/assertions_addendum.hpp" #include "iox/expected.hpp" -#include "sample_mut.hpp" -#include "service_type.hpp" -#include "unique_port_id.hpp" +#include "iox2/connection_failure.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/publisher_error.hpp" +#include "iox2/sample_mut.hpp" +#include "iox2/service_type.hpp" +#include "iox2/unique_port_id.hpp" #include namespace iox2 { -enum class PublisherCreateError : uint8_t { - /// The maximum amount of [`Publisher`]s that can connect to a - /// [`Service`](crate::service::Service) is - /// defined in [`crate::config::Config`]. When this is exceeded no more - /// [`Publisher`]s - /// can be created for a specific [`Service`](crate::service::Service). - ExceedsMaxSupportedPublishers, - /// The datasegment in which the payload of the [`Publisher`] is stored, - /// could not be created. - UnableToCreateDataSegment, -}; - -/// Defines a failure that can occur in [`Publisher::loan()`] and -/// [`Publisher::loan_uninit()`] or is part of [`PublisherSendError`] emitted in -/// [`Publisher::send_copy()`]. -enum class PublisherLoanError : uint8_t { - /// The [`Publisher`]s data segment does not have any more memory left - OutOfMemory, - /// The maximum amount of [`SampleMut`]s a user can borrow with - /// [`Publisher::loan()`] or - /// [`Publisher::loan_uninit()`] is - /// defined in [`crate::config::Config`]. When this is exceeded those calls - /// will fail. - ExceedsMaxLoanedSamples, - /// The provided slice size exceeds the configured max slice size of the - /// [`Publisher`]. - /// To send a [`SampleMut`] with this size a new [`Publisher`] has to be - /// created with - /// a - /// [`crate::service::port_factory::publisher::PortFactoryPublisher::max_slice_len()`] - /// greater or equal to the required len. - ExceedsMaxLoanSize, - /// Errors that indicate either an implementation issue or a wrongly - /// configured system. - InternalFailure, -}; - template class Publisher { public: - Publisher() = default; - Publisher(Publisher&&) = default; - auto operator=(Publisher&&) -> Publisher& = default; - ~Publisher() = default; + Publisher(Publisher&&) noexcept; + auto operator=(Publisher&&) noexcept -> Publisher&; + ~Publisher(); Publisher(const Publisher&) = delete; auto operator=(const Publisher&) -> Publisher& = delete; - auto id() const -> UniquePublisherId { - IOX_TODO(); - } - auto send_copy(const Payload& payload) const -> iox::expected { - IOX_TODO(); - } - - auto loan_uninit() -> iox::expected, PublisherLoanError> { - IOX_TODO(); - } - - auto loan() -> iox::expected, PublisherLoanError> { - IOX_TODO(); - } + auto id() const -> UniquePublisherId; + auto send_copy(const Payload& payload) const -> iox::expected; + auto loan_uninit() -> iox::expected, PublisherLoanError>; + auto loan() -> iox::expected, PublisherLoanError>; auto loan_slice(const uint64_t number_of_elements) - -> iox::expected, PublisherLoanError> { - IOX_TODO(); - } + -> iox::expected, PublisherLoanError>; auto loan_slice_uninit(const uint64_t number_of_elements) - -> iox::expected, PublisherLoanError> { - IOX_TODO(); + -> iox::expected, PublisherLoanError>; + + auto update_connections() -> iox::expected; + + private: + template + friend class PortFactoryPublisher; + + explicit Publisher(iox2_publisher_h handle); + void drop(); + + iox2_publisher_h m_handle; +}; + +template +inline Publisher::Publisher(iox2_publisher_h handle) + : m_handle { handle } { +} + +template +inline void Publisher::drop() { + if (m_handle != nullptr) { + iox2_publisher_drop(m_handle); + m_handle = nullptr; } +} - auto update_connections() -> iox::expected { - IOX_TODO(); +template +inline Publisher::Publisher(Publisher&& rhs) noexcept + : m_handle { nullptr } { + *this = std::move(rhs); +} + +template +inline auto Publisher::operator=(Publisher&& rhs) noexcept -> Publisher& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } -}; + + return *this; +} + +template +inline Publisher::~Publisher() { + drop(); +} + +template +inline auto Publisher::id() const -> UniquePublisherId { + IOX_TODO(); +} + +template +inline auto Publisher::send_copy(const Payload& payload) const + -> iox::expected { + IOX_TODO(); +} + +template +inline auto Publisher::loan_uninit() + -> iox::expected, PublisherLoanError> { + IOX_TODO(); +} + +template +inline auto +Publisher::loan() -> iox::expected, PublisherLoanError> { + IOX_TODO(); +} + +template +inline auto Publisher::loan_slice(const uint64_t number_of_elements) + -> iox::expected, PublisherLoanError> { + IOX_TODO(); +} + +template +inline auto Publisher::loan_slice_uninit(const uint64_t number_of_elements) + -> iox::expected, PublisherLoanError> { + IOX_TODO(); +} + +template +inline auto Publisher::update_connections() -> iox::expected { + IOX_TODO(); +} } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp new file mode 100644 index 000000000..519e5b9f0 --- /dev/null +++ b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp @@ -0,0 +1,58 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#ifndef IOX2_PUBLISHER_ERROR_HPP +#define IOX2_PUBLISHER_ERROR_HPP + +#include + +namespace iox2 { + +enum class PublisherCreateError : uint8_t { + /// The maximum amount of [`Publisher`]s that can connect to a + /// [`Service`](crate::service::Service) is + /// defined in [`crate::config::Config`]. When this is exceeded no more + /// [`Publisher`]s + /// can be created for a specific [`Service`](crate::service::Service). + ExceedsMaxSupportedPublishers, + /// The datasegment in which the payload of the [`Publisher`] is stored, + /// could not be created. + UnableToCreateDataSegment, +}; + +/// Defines a failure that can occur in [`Publisher::loan()`] and +/// [`Publisher::loan_uninit()`] or is part of [`PublisherSendError`] emitted in +/// [`Publisher::send_copy()`]. +enum class PublisherLoanError : uint8_t { + /// The [`Publisher`]s data segment does not have any more memory left + OutOfMemory, + /// The maximum amount of [`SampleMut`]s a user can borrow with + /// [`Publisher::loan()`] or + /// [`Publisher::loan_uninit()`] is + /// defined in [`crate::config::Config`]. When this is exceeded those calls + /// will fail. + ExceedsMaxLoanedSamples, + /// The provided slice size exceeds the configured max slice size of the + /// [`Publisher`]. + /// To send a [`SampleMut`] with this size a new [`Publisher`] has to be + /// created with + /// a + /// [`crate::service::port_factory::publisher::PortFactoryPublisher::max_slice_len()`] + /// greater or equal to the required len. + ExceedsMaxLoanSize, + /// Errors that indicate either an implementation issue or a wrongly + /// configured system. + InternalFailure, +}; +} // namespace iox2 + +#endif diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs index aafc8f2b4..6905be0d7 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs @@ -32,7 +32,7 @@ use core::mem::ManuallyDrop; #[derive(Copy, Clone)] pub enum iox2_publisher_create_error_e { EXCEEDS_MAX_SUPPORTED_PUBLISHERS = IOX2_OK as isize + 1, - UNABLE_TO_CREAT_DATA_SEGMENT, + UNABLE_TO_CREATE_DATA_SEGMENT, } impl IntoCInt for PublisherCreateError { @@ -42,7 +42,7 @@ impl IntoCInt for PublisherCreateError { iox2_publisher_create_error_e::EXCEEDS_MAX_SUPPORTED_PUBLISHERS } PublisherCreateError::UnableToCreateDataSegment => { - iox2_publisher_create_error_e::UNABLE_TO_CREAT_DATA_SEGMENT + iox2_publisher_create_error_e::UNABLE_TO_CREATE_DATA_SEGMENT } }) as c_int } diff --git a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs index dad8f1d48..a6e73ddee 100644 --- a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs +++ b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs @@ -19,8 +19,8 @@ use crate::{ iox2_event_open_or_create_error_e, iox2_listener_create_error_e, iox2_listener_wait_error_e, iox2_node_creation_failure_e, iox2_node_event_e, iox2_node_list_failure_e, iox2_notifier_create_error_e, iox2_notifier_notify_error_e, - iox2_pub_sub_open_or_create_error_e, iox2_semantic_string_error_e, - iox2_service_details_error_e, iox2_type_detail_error_e, + iox2_pub_sub_open_or_create_error_e, iox2_publisher_create_error_e, + iox2_semantic_string_error_e, iox2_service_details_error_e, iox2_type_detail_error_e, }; #[doc(hidden)] @@ -114,3 +114,11 @@ pub unsafe extern "C" fn __iox2_internal_notifier_notify_error_stub() -> iox2_no pub unsafe extern "C" fn __iox2_internal_listener_wait_error_stub() -> iox2_listener_wait_error_e { iox2_listener_wait_error_e::INTERNAL_FAILURE } + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_publisher_create_error_stub( +) -> iox2_publisher_create_error_e { + iox2_publisher_create_error_e::EXCEEDS_MAX_SUPPORTED_PUBLISHERS +} From b16d31b1db2391ca2b5a76179481ecf8551da065 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 15:17:04 +0200 Subject: [PATCH 07/35] [#264] Implement cxx subscriber --- .../cxx/include/iox2/enum_translation.hpp | 14 +++ .../include/iox2/port_factory_subscriber.hpp | 9 +- iceoryx2-ffi/cxx/include/iox2/subscriber.hpp | 113 ++++++++++++------ .../cxx/include/iox2/subscriber_error.hpp | 35 ++++++ iceoryx2-ffi/ffi/src/api/quirks_correction.rs | 11 +- 5 files changed, 144 insertions(+), 38 deletions(-) create mode 100644 iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index 56c7c084e..e10ba7dd1 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -28,6 +28,7 @@ #include "iox2/service_builder_publish_subscribe_error.hpp" #include "iox2/service_error_enums.hpp" #include "iox2/service_type.hpp" +#include "iox2/subscriber_error.hpp" namespace iox { template <> @@ -474,6 +475,19 @@ constexpr auto from(const int value) noexcept - IOX_UNREACHABLE(); } + +template <> +constexpr auto from(const int value) noexcept -> iox2::SubscriberCreateError { + const auto error = static_cast(value); + switch (error) { + case iox2_subscriber_create_error_e_BUFFER_SIZE_EXCEEDS_MAX_SUPPORTED_BUFFER_SIZE_OF_SERVICE: + return iox2::SubscriberCreateError::BufferSizeExceedsMaxSupportedBufferSizeOfService; + case iox2_subscriber_create_error_e_EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS: + return iox2::SubscriberCreateError::ExceedsMaxSupportedSubscribers; + } + + IOX_UNREACHABLE(); +} } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp index 65e3e08ab..c55484937 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp @@ -60,7 +60,14 @@ PortFactorySubscriber::create() && -> iox::expected(sub_handle)); + } + + return iox::err(iox::into(result)); } } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp index 26d9a3615..1f5e53aa7 100644 --- a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp @@ -13,56 +13,97 @@ #ifndef IOX2_SUBSCRIBER_HPP #define IOX2_SUBSCRIBER_HPP -#include "connection_failure.hpp" #include "iox/assertions_addendum.hpp" #include "iox/expected.hpp" #include "iox/optional.hpp" -#include "sample.hpp" -#include "service_type.hpp" -#include "unique_port_id.hpp" - -#include +#include "iox2/connection_failure.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/sample.hpp" +#include "iox2/service_type.hpp" +#include "iox2/subscriber_error.hpp" +#include "iox2/unique_port_id.hpp" namespace iox2 { -enum class SubscriberReceiveError : uint8_t { -}; - -enum class SubscriberCreateError : uint8_t { - /// The maximum amount of [`Subscriber`]s that can connect to a - /// [`Service`](crate::service::Service) is - /// defined in [`crate::config::Config`]. When this is exceeded no more - /// [`Subscriber`]s - /// can be created for a specific [`Service`](crate::service::Service). - ExceedsMaxSupportedSubscribers, - /// When the [`Subscriber`] requires a larger buffer size than the - /// [`Service`](crate::service::Service) offers the creation will fail. - BufferSizeExceedsMaxSupportedBufferSizeOfService, -}; - template class Subscriber { public: - Subscriber() = default; - Subscriber(Subscriber&&) = default; - auto operator=(Subscriber&&) -> Subscriber& = default; - ~Subscriber() = default; + Subscriber(Subscriber&&) noexcept; + auto operator=(Subscriber&&) noexcept -> Subscriber&; + ~Subscriber(); Subscriber(const Subscriber&) = delete; auto operator=(const Subscriber&) -> Subscriber& = delete; - auto id() const -> UniqueSubscriberId { - IOX_TODO(); - } - auto buffer_size() const -> uint64_t { - IOX_TODO(); - } - auto receive() const -> iox::expected>, SubscriberReceiveError> { - IOX_TODO(); + auto id() const -> UniqueSubscriberId; + auto buffer_size() const -> uint64_t; + auto receive() const -> iox::expected>, SubscriberReceiveError>; + auto update_connections() const -> iox::expected; + + private: + template + friend class PortFactorySubscriber; + + explicit Subscriber(iox2_subscriber_h handle); + void drop(); + + iox2_subscriber_h m_handle; +}; +template +inline Subscriber::Subscriber(iox2_subscriber_h handle) + : m_handle { handle } { +} + +template +inline Subscriber::Subscriber(Subscriber&& rhs) noexcept + : m_handle { nullptr } { + *this = std::move(rhs); +} + +template +inline auto Subscriber::operator=(Subscriber&& rhs) noexcept -> Subscriber& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } - auto update_connections() const -> iox::expected { - IOX_TODO(); + + return *this; +} + +template +inline Subscriber::~Subscriber() { + drop(); +} + +template +inline void Subscriber::drop() { + if (m_handle != nullptr) { + iox2_subscriber_drop(m_handle); + m_handle = nullptr; } -}; +} + +template +inline auto Subscriber::id() const -> UniqueSubscriberId { + IOX_TODO(); +} + +template +inline auto Subscriber::buffer_size() const -> uint64_t { + IOX_TODO(); +} + +template +inline auto Subscriber::receive() const + -> iox::expected>, SubscriberReceiveError> { + IOX_TODO(); +} + +template +inline auto Subscriber::update_connections() const -> iox::expected { + IOX_TODO(); +} + } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp new file mode 100644 index 000000000..4c3655dea --- /dev/null +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp @@ -0,0 +1,35 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#ifndef IOX2_SUBSCRIBER_ERROR_HPP +#define IOX2_SUBSCRIBER_ERROR_HPP + +#include + +namespace iox2 { +enum class SubscriberReceiveError : uint8_t { +}; + +enum class SubscriberCreateError : uint8_t { + /// The maximum amount of [`Subscriber`]s that can connect to a + /// [`Service`](crate::service::Service) is + /// defined in [`crate::config::Config`]. When this is exceeded no more + /// [`Subscriber`]s + /// can be created for a specific [`Service`](crate::service::Service). + ExceedsMaxSupportedSubscribers, + /// When the [`Subscriber`] requires a larger buffer size than the + /// [`Service`](crate::service::Service) offers the creation will fail. + BufferSizeExceedsMaxSupportedBufferSizeOfService, +}; +} // namespace iox2 + +#endif diff --git a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs index a6e73ddee..54c2bf0ec 100644 --- a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs +++ b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs @@ -20,7 +20,8 @@ use crate::{ iox2_node_creation_failure_e, iox2_node_event_e, iox2_node_list_failure_e, iox2_notifier_create_error_e, iox2_notifier_notify_error_e, iox2_pub_sub_open_or_create_error_e, iox2_publisher_create_error_e, - iox2_semantic_string_error_e, iox2_service_details_error_e, iox2_type_detail_error_e, + iox2_semantic_string_error_e, iox2_service_details_error_e, iox2_subscriber_create_error_e, + iox2_type_detail_error_e, }; #[doc(hidden)] @@ -122,3 +123,11 @@ pub unsafe extern "C" fn __iox2_internal_publisher_create_error_stub( ) -> iox2_publisher_create_error_e { iox2_publisher_create_error_e::EXCEEDS_MAX_SUPPORTED_PUBLISHERS } + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_subscriber_create_error_stub( +) -> iox2_subscriber_create_error_e { + iox2_subscriber_create_error_e::EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS +} From 7ca62a815bd02b68d08c812342a6e746b81801ff Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 16:53:17 +0200 Subject: [PATCH 08/35] [#264] Implement cxx publisher send copy --- .../cxx/include/iox2/enum_translation.hpp | 23 +++ iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 12 +- .../cxx/include/iox2/publisher_error.hpp | 27 ++++ iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp | 19 +-- iceoryx2-ffi/ffi/src/api/publisher.rs | 133 +++++++++++++++++- iceoryx2-ffi/ffi/src/api/quirks_correction.rs | 12 +- 6 files changed, 204 insertions(+), 22 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index e10ba7dd1..0e39e5748 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -488,6 +488,29 @@ constexpr auto from(const int value) noexcept IOX_UNREACHABLE(); } + +template <> +constexpr auto from(const int value) noexcept -> iox2::PublisherSendError { + const auto error = static_cast(value); + switch (error) { + case iox2_publisher_send_error_e_CONNECTION_BROKEN_SINCE_PUBLISHER_NO_LONGER_EXISTS: + return iox2::PublisherSendError::ConnectionBrokenSincePublisherNoLongerExists; + case iox2_publisher_send_error_e_CONNECTION_CORRUPTED: + return iox2::PublisherSendError::ConnectionCorrupted; + case iox2_publisher_send_error_e_LOAN_ERROR_OUT_OF_MEMORY: + return iox2::PublisherSendError::LoanErrorOutOfMemory; + case iox2_publisher_send_error_e_LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES: + return iox2::PublisherSendError::LoanErrorExceedsMaxLoanedSamples; + case iox2_publisher_send_error_e_LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE: + return iox2::PublisherSendError::LoanErrorInternalFailure; + case iox2_publisher_send_error_e_LOAN_ERROR_INTERNAL_FAILURE: + return iox2::PublisherSendError::LoanErrorInternalFailure; + case iox2_publisher_send_error_e_CONNECTION_ERROR: + return iox2::PublisherSendError::ConnectionError; + } + + IOX_UNREACHABLE(); +} } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index c23aa0e84..438855a8c 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -100,7 +100,17 @@ inline auto Publisher::id() const -> UniquePublisherId { template inline auto Publisher::send_copy(const Payload& payload) const -> iox::expected { - IOX_TODO(); + auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); + + size_t number_of_recipients = 0; + auto result = iox2_publisher_send_copy( + ref_handle, static_cast(&payload), sizeof(Payload), &number_of_recipients); + + if (result == IOX2_OK) { + return iox::ok(number_of_recipients); + } + + return iox::err(iox::into(result)); } template diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp index 519e5b9f0..2e65e9444 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp @@ -53,6 +53,33 @@ enum class PublisherLoanError : uint8_t { /// configured system. InternalFailure, }; + +/// Failure that can be emitted when a [`SampleMut`] is sent via [`SampleMut::send()`]. +enum class PublisherSendError : uint8_t { + /// [`SampleMut::send()`] was called but the corresponding [`Publisher`] went already out of + /// scope. + ConnectionBrokenSincePublisherNoLongerExists, + /// A connection between a [`Subscriber`] and a + /// [`Publisher`] is corrupted. + ConnectionCorrupted, + /// A failure occurred while acquiring memory for the payload + /// The [`Publisher`]s data segment does not have any more memory left + LoanErrorOutOfMemory, + /// The maximum amount of [`SampleMut`]s a user can borrow with [`Publisher::loan()`] or + /// [`Publisher::loan_uninit()`] is + /// defined in [`crate::config::Config`]. When this is exceeded those calls will fail. + LoanErrorExceedsMaxLoanedSamples, + /// The provided slice size exceeds the configured max slice size of the [`Publisher`]. + /// To send a [`SampleMut`] with this size a new [`Publisher`] has to be created with + /// a [`crate::service::port_factory::publisher::PortFactoryPublisher::max_slice_len()`] + /// greater or equal to the required len. + LoanErrorExceedsMaxLoanSize, + /// Errors that indicate either an implementation issue or a wrongly configured system. + LoanErrorInternalFailure, + /// A failure occurred while establishing a connection to a + /// [`Subscriber`] + ConnectionError, +}; } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index 573471779..2c0199b11 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -18,29 +18,12 @@ #include "iox/expected.hpp" #include "iox/function.hpp" #include "iox/slice.hpp" +#include "iox2/publisher_error.hpp" #include "service_type.hpp" #include namespace iox2 { -/// Failure that can be emitted when a [`SampleMut`] is sent via -/// [`SampleMut::send()`]. -enum PublisherSendError : uint8_t { - /// [`SampleMut::send()`] was called but the corresponding [`Publisher`] - /// went already out of - /// scope. - ConnectionBrokenSincePublisherNoLongerExists, - /// A connection between a - /// [`Subscriber`](crate::port::subscriber::Subscriber) and a - /// [`Publisher`] is corrupted. - ConnectionCorrupted, - /// A failure occurred while acquiring memory for the payload - LoanError, - /// A failure occurred while establishing a connection to a - /// [`Subscriber`](crate::port::subscriber::Subscriber) - ConnectionError, -}; - template class SampleMut { public: diff --git a/iceoryx2-ffi/ffi/src/api/publisher.rs b/iceoryx2-ffi/ffi/src/api/publisher.rs index dab15dd04..185a3bb70 100644 --- a/iceoryx2-ffi/ffi/src/api/publisher.rs +++ b/iceoryx2-ffi/ffi/src/api/publisher.rs @@ -13,16 +13,75 @@ #![allow(non_camel_case_types)] use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::IOX2_OK; -use iceoryx2::port::publisher::Publisher; +use iceoryx2::port::publisher::{Publisher, PublisherLoanError, PublisherSendError}; use iceoryx2::prelude::*; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; +use core::ffi::{c_int, c_void}; use core::mem::ManuallyDrop; // BEGIN types definition +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_publisher_send_error_e { + CONNECTION_BROKEN_SINCE_PUBLISHER_NO_LONGER_EXISTS = IOX2_OK as isize + 1, + CONNECTION_CORRUPTED, + LOAN_ERROR_OUT_OF_MEMORY, + LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES, + LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE, + LOAN_ERROR_INTERNAL_FAILURE, + CONNECTION_ERROR, +} + +impl IntoCInt for PublisherSendError { + fn into_c_int(self) -> c_int { + (match self { + PublisherSendError::ConnectionBrokenSincePublisherNoLongerExists => { + iox2_publisher_send_error_e::CONNECTION_BROKEN_SINCE_PUBLISHER_NO_LONGER_EXISTS + } + PublisherSendError::ConnectionCorrupted => { + iox2_publisher_send_error_e::CONNECTION_CORRUPTED + } + PublisherSendError::LoanError(PublisherLoanError::OutOfMemory) => { + iox2_publisher_send_error_e::LOAN_ERROR_OUT_OF_MEMORY + } + PublisherSendError::LoanError(PublisherLoanError::ExceedsMaxLoanedSamples) => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES + } + PublisherSendError::LoanError(PublisherLoanError::ExceedsMaxLoanSize) => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE + } + PublisherSendError::LoanError(PublisherLoanError::InternalFailure) => { + iox2_publisher_send_error_e::LOAN_ERROR_INTERNAL_FAILURE + } + PublisherSendError::ConnectionError(_) => iox2_publisher_send_error_e::CONNECTION_ERROR, + }) as c_int + } +} + +impl IntoCInt for PublisherLoanError { + fn into_c_int(self) -> c_int { + (match self { + PublisherLoanError::OutOfMemory => { + iox2_publisher_send_error_e::LOAN_ERROR_OUT_OF_MEMORY + } + PublisherLoanError::ExceedsMaxLoanedSamples => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES + } + PublisherLoanError::ExceedsMaxLoanSize => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE + } + PublisherLoanError::InternalFailure => { + iox2_publisher_send_error_e::LOAN_ERROR_INTERNAL_FAILURE + } + }) as c_int + } +} + pub(super) union PublisherUnion { ipc: ManuallyDrop>, local: ManuallyDrop>, @@ -98,8 +157,78 @@ impl HandleToType for iox2_publisher_ref_h { // END type definition +unsafe fn send_copy( + publisher: &Publisher, + data_ptr: *const c_void, + data_len: usize, + number_of_recipients: *mut usize, +) -> c_int { + // loan_slice_uninit(1) <= 1 is correct here since it defines the number of + // slice elements not bytes. The element was set via TypeDetails and has a + // defined size and alignment. + let mut sample = match publisher.loan_slice_uninit(1) { + Ok(sample) => sample, + Err(e) => return e.into_c_int(), + }; + + if sample.payload().len() < data_len { + return iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE as c_int; + } + + let sample_ptr = sample.payload_mut().as_mut_ptr(); + core::ptr::copy_nonoverlapping(data_ptr, sample_ptr.cast(), data_len); + match sample.assume_init().send() { + Ok(v) => { + if !number_of_recipients.is_null() { + *number_of_recipients = v; + } + } + Err(e) => return e.into_c_int(), + } + + IOX2_OK +} + // BEGIN C API +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_publisher_ref_h( + handle: iox2_publisher_h, +) -> iox2_publisher_ref_h { + debug_assert!(!handle.is_null()); + + (*handle.as_type()).as_ref_handle() as *mut _ as _ +} + +#[no_mangle] +pub unsafe extern "C" fn iox2_publisher_send_copy( + publisher_handle: iox2_publisher_ref_h, + data_ptr: *const c_void, + data_len: usize, + number_of_recipients: *mut usize, +) -> c_int { + debug_assert!(!publisher_handle.is_null()); + debug_assert!(!data_ptr.is_null()); + debug_assert!(data_len != 0); + + let publisher = &mut *publisher_handle.as_type(); + + match publisher.service_type { + iox2_service_type_e::IPC => send_copy( + &publisher.value.as_mut().ipc, + data_ptr, + data_len, + number_of_recipients, + ), + iox2_service_type_e::LOCAL => send_copy( + &publisher.value.as_mut().local, + data_ptr, + data_len, + number_of_recipients, + ), + } +} + /// This function needs to be called to destroy the publisher! /// /// # Arguments @@ -133,6 +262,8 @@ pub unsafe extern "C" fn iox2_publisher_drop(publisher_handle: iox2_publisher_h) use core::time::Duration; use iceoryx2_bb_log::set_log_level; +use super::IntoCInt; + const CYCLE_TIME: Duration = Duration::from_secs(1); #[no_mangle] diff --git a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs index 54c2bf0ec..6f22001d3 100644 --- a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs +++ b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs @@ -20,8 +20,8 @@ use crate::{ iox2_node_creation_failure_e, iox2_node_event_e, iox2_node_list_failure_e, iox2_notifier_create_error_e, iox2_notifier_notify_error_e, iox2_pub_sub_open_or_create_error_e, iox2_publisher_create_error_e, - iox2_semantic_string_error_e, iox2_service_details_error_e, iox2_subscriber_create_error_e, - iox2_type_detail_error_e, + iox2_publisher_send_error_e, iox2_semantic_string_error_e, iox2_service_details_error_e, + iox2_subscriber_create_error_e, iox2_type_detail_error_e, }; #[doc(hidden)] @@ -131,3 +131,11 @@ pub unsafe extern "C" fn __iox2_internal_subscriber_create_error_stub( ) -> iox2_subscriber_create_error_e { iox2_subscriber_create_error_e::EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS } + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_publisher_send_error_stub() -> iox2_publisher_send_error_e +{ + iox2_publisher_send_error_e::CONNECTION_ERROR +} From 19e92aee0c3d918d7bace6167f2127a99589b620 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 25 Jul 2024 17:30:20 +0200 Subject: [PATCH 09/35] [#210] Add functionality to receive samples --- iceoryx2-ffi/ffi/src/api/mod.rs | 2 + iceoryx2-ffi/ffi/src/api/sample.rs | 129 ++++++++++++++++++++++ iceoryx2-ffi/ffi/src/api/subscriber.rs | 145 ++++++++++++++++++++++++- 3 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 iceoryx2-ffi/ffi/src/api/sample.rs diff --git a/iceoryx2-ffi/ffi/src/api/mod.rs b/iceoryx2-ffi/ffi/src/api/mod.rs index 55b41a33d..d37948fdd 100644 --- a/iceoryx2-ffi/ffi/src/api/mod.rs +++ b/iceoryx2-ffi/ffi/src/api/mod.rs @@ -33,6 +33,7 @@ mod port_factory_publisher_builder; mod port_factory_subscriber_builder; mod publisher; mod quirks_correction; +mod sample; mod service; mod service_builder; mod service_builder_event; @@ -56,6 +57,7 @@ pub use port_factory_publisher_builder::*; pub use port_factory_subscriber_builder::*; pub use publisher::*; pub use quirks_correction::*; +pub use sample::*; pub use service::*; pub use service_builder::*; pub use service_builder_event::*; diff --git a/iceoryx2-ffi/ffi/src/api/sample.rs b/iceoryx2-ffi/ffi/src/api/sample.rs new file mode 100644 index 000000000..dfe97f804 --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/sample.rs @@ -0,0 +1,129 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; + +use iceoryx2::prelude::*; +use iceoryx2::sample::Sample; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::mem::ManuallyDrop; + +// BEGIN types definition + +pub(super) union SampleUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl SampleUnion { + pub(super) fn new_ipc(sample: Sample) -> Self { + Self { + ipc: ManuallyDrop::new(sample), + } + } + pub(super) fn new_local( + sample: Sample, + ) -> Self { + Self { + local: ManuallyDrop::new(sample), + } + } +} + +#[repr(C)] +#[repr(align(8))] // alignment of Option +pub struct iox2_sample_storage_t { + internal: [u8; 80], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(SampleUnion)] +pub struct iox2_sample_t { + service_type: iox2_service_type_e, + value: iox2_sample_storage_t, + deleter: fn(*mut iox2_sample_t), +} + +impl iox2_sample_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: SampleUnion, + deleter: fn(*mut iox2_sample_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_sample_h_t; +/// The owning handle for `iox2_sample_t`. Passing the handle to an function transfers the ownership. +pub type iox2_sample_h = *mut iox2_sample_h_t; + +pub struct iox2_sample_ref_h_t; +/// The non-owning handle for `iox2_sample_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_sample_ref_h = *mut iox2_sample_ref_h_t; + +impl HandleToType for iox2_sample_h { + type Target = *mut iox2_sample_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_sample_ref_h { + type Target = *mut iox2_sample_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +/// This function needs to be called to destroy the sample! +/// +/// # Arguments +/// +/// * `sample_handle` - A valid [`iox2_sample_h`] +/// +/// # Safety +/// +/// * The `sample_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_sample_t`] can be re-used with a call to +/// [`iox2_subscriber_receive`](crate::iox2_subscriber_receive)! +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_drop(sample_handle: iox2_sample_h) { + debug_assert!(!sample_handle.is_null()); + + let sample = &mut *sample_handle.as_type(); + + match sample.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut sample.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut sample.value.as_mut().local); + } + } + (sample.deleter)(sample); +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/subscriber.rs b/iceoryx2-ffi/ffi/src/api/subscriber.rs index b9c6af151..78a4f7d58 100644 --- a/iceoryx2-ffi/ffi/src/api/subscriber.rs +++ b/iceoryx2-ffi/ffi/src/api/subscriber.rs @@ -12,17 +12,41 @@ #![allow(non_camel_case_types)] -use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::api::{ + c_size_t, iox2_sample_h, iox2_sample_t, iox2_service_type_e, HandleToType, IntoCInt, + NoUserHeaderFfi, PayloadFfi, SampleUnion, IOX2_OK, +}; -use iceoryx2::port::subscriber::Subscriber; +use iceoryx2::port::subscriber::{Subscriber, SubscriberReceiveError}; use iceoryx2::prelude::*; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; +use core::ffi::c_int; use core::mem::ManuallyDrop; // BEGIN types definition +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_subscriber_receive_error_e { + EXCEEDS_MAX_BORROWED_SAMPLES = IOX2_OK as isize + 1, + CONNECTION_FAILURE, +} + +impl IntoCInt for SubscriberReceiveError { + fn into_c_int(self) -> c_int { + (match self { + SubscriberReceiveError::ExceedsMaxBorrowedSamples => { + iox2_subscriber_receive_error_e::EXCEEDS_MAX_BORROWED_SAMPLES + } + SubscriberReceiveError::ConnectionFailure(_) => { + iox2_subscriber_receive_error_e::CONNECTION_FAILURE + } + }) as c_int + } +} + pub(super) union SubscriberUnion { ipc: ManuallyDrop>, local: ManuallyDrop>, @@ -100,6 +124,123 @@ impl HandleToType for iox2_subscriber_ref_h { // BEGIN C API +/// This function casts an owning [`iox2_subscriber_h`] into a non-owning [`iox2_subscriber_ref_h`] +/// +/// # Arguments +/// +/// * `subscriber_handle` obtained by [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create) +/// +/// Returns a [`iox2_subscriber_ref_h`] +/// +/// # Safety +/// +/// * The `subscriber_handle` must be a valid handle. +/// * The `subscriber_handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_subscriber_ref_h( + subscriber_handle: iox2_subscriber_h, +) -> iox2_subscriber_ref_h { + debug_assert!(!subscriber_handle.is_null()); + + (*subscriber_handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Returns the buffer size of the subscriber +/// +/// # Arguments +/// +/// * `subscriber_handle` - Must be a valid [`iox2_subscriber_ref_h`] +/// obtained by [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create) and +/// casted by [`iox2_cast_subscriber_ref_h`]. +/// +/// # Safety +/// +/// * `subscriber_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_subscriber_buffer_size( + subscriber_handle: iox2_subscriber_ref_h, +) -> c_size_t { + debug_assert!(!subscriber_handle.is_null()); + + let subscriber = &mut *subscriber_handle.as_type(); + + match subscriber.service_type { + iox2_service_type_e::IPC => subscriber.value.as_ref().ipc.buffer_size(), + iox2_service_type_e::LOCAL => subscriber.value.as_ref().local.buffer_size(), + } +} + +// TODO [#210] add all the other setter methods + +/// Takes a sample ouf of the subscriber queue. +/// +/// # Arguments +/// +/// * `subscriber_handle` - Must be a valid [`iox2_subscriber_ref_h`] +/// obtained by [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create) and +/// casted by [`iox2_cast_subscriber_ref_h`]. +/// * `sample_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_sample_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// * `sample_handle_ptr` - An uninitialized or dangling [`iox2_sample_h`] handle which will be initialized by this function call if a sample is obtained, otherwise it will be set to NULL. +/// +/// Returns IOX2_OK on success, an [`SubscriberReceiveError`] otherwise. +/// Attention, an empty subscriber queue is not an error and even with IOX2_OK it is possible to get a NULL in `sample_handle_ptr`. +/// +/// # Safety +/// +/// * The `subscriber_handle` is still valid after the return of this function and can be use in another function call. +#[no_mangle] +pub unsafe extern "C" fn iox2_subscriber_receive( + subscriber_handle: iox2_subscriber_ref_h, + sample_struct_ptr: *mut iox2_sample_t, + sample_handle_ptr: *mut iox2_sample_h, +) -> c_int { + debug_assert!(!subscriber_handle.is_null()); + debug_assert!(!sample_handle_ptr.is_null()); + + *sample_handle_ptr = std::ptr::null_mut(); + + let mut sample_struct_ptr = sample_struct_ptr; + fn no_op(_: *mut iox2_sample_t) {} + let mut deleter: fn(*mut iox2_sample_t) = no_op; + if sample_struct_ptr.is_null() { + sample_struct_ptr = iox2_sample_t::alloc(); + deleter = iox2_sample_t::dealloc; + } + debug_assert!(!sample_struct_ptr.is_null()); + + let subscriber = &mut *subscriber_handle.as_type(); + + match subscriber.service_type { + iox2_service_type_e::IPC => match subscriber.value.as_ref().ipc.receive() { + Ok(Some(sample)) => { + (*sample_struct_ptr).init( + subscriber.service_type, + SampleUnion::new_ipc(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + } + Ok(None) => (), + Err(error) => return error.into_c_int(), + }, + iox2_service_type_e::LOCAL => match subscriber.value.as_ref().local.receive() { + Ok(Some(sample)) => { + (*sample_struct_ptr).init( + subscriber.service_type, + SampleUnion::new_local(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + } + Ok(None) => (), + Err(error) => return error.into_c_int(), + }, + } + + IOX2_OK +} + /// This function needs to be called to destroy the subscriber! /// /// # Arguments From 2a2787e29d277a70d6aab966ad9f535391967001 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 18:25:13 +0200 Subject: [PATCH 10/35] [#264] Fix publisher slice len issue with custom payload message type details --- .../cxx/publish_subscribe/src/publisher.cpp | 8 +++--- iceoryx2/src/port/publisher.rs | 19 ++++++++++++-- iceoryx2/tests/publisher_tests.rs | 26 +++++++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/examples/cxx/publish_subscribe/src/publisher.cpp b/examples/cxx/publish_subscribe/src/publisher.cpp index d5d41cce7..087f68a69 100644 --- a/examples/cxx/publish_subscribe/src/publisher.cpp +++ b/examples/cxx/publish_subscribe/src/publisher.cpp @@ -36,11 +36,13 @@ auto main() -> int { auto counter = 0; while (node.wait(CYCLE_TIME) == NodeEvent::Tick) { counter += 1; - auto sample = publisher.loan_uninit().expect("acquire sample"); + publisher.send_copy(TransmissionData { counter, counter * 3, counter * 812.12 }).expect("successful send"); - sample.write_payload(TransmissionData { counter, counter * 3, counter * 812.12 }); // NOLINT + // auto sample = publisher.loan_uninit().expect("acquire sample"); - send_sample(std::move(sample)).expect("send successful"); + // sample.write_payload(TransmissionData { counter, counter * 3, counter * 812.12 }); // NOLINT + + // send_sample(std::move(sample)).expect("send successful"); std::cout << "Send sample " << counter << "..." << std::endl; } diff --git a/iceoryx2/src/port/publisher.rs b/iceoryx2/src/port/publisher.rs index b09788338..9768b074b 100644 --- a/iceoryx2/src/port/publisher.rs +++ b/iceoryx2/src/port/publisher.rs @@ -970,7 +970,8 @@ impl slice_len, max_slice_len); } - let chunk = self.allocate(self.sample_layout(slice_len))?; + let sample_layout = self.sample_layout(slice_len); + let chunk = self.allocate(sample_layout)?; let header_ptr = chunk.data_ptr as *mut Header; let user_header_ptr = self.user_header_ptr(header_ptr) as *mut UserHeader; let payload_ptr = self.payload_ptr(header_ptr) as *mut MaybeUninit; @@ -982,11 +983,25 @@ impl )) }; + let payload_size = self + .data_segment + .subscriber_connections + .static_config + .message_type_details + .payload + .size; + + let slice_len_adjusted_to_payload_type_details = + payload_size * slice_len / core::mem::size_of::(); + let sample = unsafe { RawSampleMut::new_unchecked( header_ptr, user_header_ptr, - core::slice::from_raw_parts_mut(payload_ptr, slice_len), + core::slice::from_raw_parts_mut( + payload_ptr, + slice_len_adjusted_to_payload_type_details, + ), ) }; diff --git a/iceoryx2/tests/publisher_tests.rs b/iceoryx2/tests/publisher_tests.rs index df60cf499..8203bd448 100644 --- a/iceoryx2/tests/publisher_tests.rs +++ b/iceoryx2/tests/publisher_tests.rs @@ -19,6 +19,7 @@ mod publisher { use iceoryx2::port::publisher::{PublisherCreateError, PublisherLoanError}; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::publisher::UnableToDeliverStrategy; + use iceoryx2::service::static_config::message_type_details::{TypeDetail, TypeVariant}; use iceoryx2::service::{service_name::ServiceName, Service}; use iceoryx2_bb_posix::barrier::*; use iceoryx2_bb_posix::unique_system_id::UniqueSystemId; @@ -377,6 +378,31 @@ mod publisher { } } + #[test] + fn publisher_with_overridden_payload_details_adjusts_slice_len() -> TestResult<()> + { + const TYPE_SIZE_OVERRIDE: usize = 128; + let service_name = generate_name()?; + let node = NodeBuilder::new().create::().unwrap(); + let mut type_detail = TypeDetail::__internal_new::(TypeVariant::FixedSize); + type_detail.size = TYPE_SIZE_OVERRIDE; + + let service = unsafe { + node.service_builder(&service_name) + .publish_subscribe::<[u8]>() + .__internal_set_payload_type_details(type_detail) + .create()? + }; + + let sut = service.publisher_builder().create()?; + + let sample = sut.loan_slice(1)?; + + assert_that!(sample.payload(), len TYPE_SIZE_OVERRIDE); + + Ok(()) + } + #[instantiate_tests()] mod zero_copy {} From 3ff3ce6411b7b4ca0547e826cf6269d393af132d Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 19:22:20 +0200 Subject: [PATCH 11/35] [#264] Implement cxx sample --- iceoryx2-ffi/cxx/include/iox2/sample.hpp | 129 ++++++++++++++++------- iceoryx2-ffi/ffi/src/api/sample.rs | 29 +++++ 2 files changed, 120 insertions(+), 38 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/sample.hpp b/iceoryx2-ffi/cxx/include/iox2/sample.hpp index 994d6b1be..54f9c02f7 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample.hpp @@ -13,59 +13,112 @@ #ifndef IOX2_SAMPLE_HPP #define IOX2_SAMPLE_HPP -#include "header_publish_subscribe.hpp" #include "iox/assertions_addendum.hpp" -#include "service_type.hpp" -#include "unique_port_id.hpp" +#include "iox2/header_publish_subscribe.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/service_type.hpp" +#include "iox2/unique_port_id.hpp" namespace iox2 { - template class Sample { public: - Sample() = default; - Sample(Sample&&) = default; - auto operator=(Sample&&) -> Sample& = default; - ~Sample() = default; + Sample(Sample&&) noexcept; + auto operator=(Sample&&) noexcept -> Sample&; + ~Sample(); Sample(const Sample&) = delete; auto operator=(const Sample&) -> Sample& = delete; - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto user_header() const -> const UserHeader& { - IOX_TODO(); - } - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto origin() const -> UniquePublisherId { - IOX_TODO(); - } -}; + auto operator*() const -> const Payload&; + auto operator->() const -> const Payload*; -template -class Sample { - public: - Sample() = default; - Sample(Sample&&) = default; - auto operator=(Sample&&) -> Sample& = default; - ~Sample() = default; + auto payload() const -> const Payload&; + template , T>> + auto user_header() const -> const T&; + auto header() const -> const HeaderPublishSubscribe&; + auto origin() const -> UniquePublisherId; - Sample(const Sample&) = delete; - auto operator=(const Sample&) -> Sample& = delete; + private: + explicit Sample(iox2_sample_h handle); + void drop(); - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); + iox2_sample_h m_handle; +}; + +template +inline Sample::Sample(iox2_sample_h handle) + : m_handle { handle } { +} + +template +inline void Sample::drop() { + if (m_handle != nullptr) { + iox2_sample_drop(m_handle); + m_handle = nullptr; } - auto origin() const -> UniquePublisherId { - IOX_TODO(); +} + +template +inline Sample::Sample(Sample&& rhs) noexcept + : m_handle { nullptr } { + *this = std::move(rhs); +} + +template +inline auto Sample::operator=(Sample&& rhs) noexcept -> Sample& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } -}; + + return *this; +} + +template +inline Sample::~Sample() { + drop(); +} + +template +inline auto Sample::operator*() const -> const Payload& { + return payload(); +} + +template +inline auto Sample::operator->() const -> const Payload* { + return &payload(); +} + +template +inline auto Sample::payload() const -> const Payload& { + auto* ref_handle = iox2_cast_sample_ref_h(m_handle); + const Payload* payload_ptr = nullptr; + size_t payload_len = 0; + + iox2_sample_payload(ref_handle, reinterpret_cast(&payload_ptr), &payload_len); + IOX_ASSERT(sizeof(Payload) <= payload_len, ""); + + return *payload_ptr; +} + +template +template +inline auto Sample::user_header() const -> const T& { + IOX_TODO(); +} + +template +inline auto Sample::header() const -> const HeaderPublishSubscribe& { + IOX_TODO(); +} + +template +inline auto Sample::origin() const -> UniquePublisherId { + IOX_TODO(); +} + } // namespace iox2 diff --git a/iceoryx2-ffi/ffi/src/api/sample.rs b/iceoryx2-ffi/ffi/src/api/sample.rs index dfe97f804..a5ddf364d 100644 --- a/iceoryx2-ffi/ffi/src/api/sample.rs +++ b/iceoryx2-ffi/ffi/src/api/sample.rs @@ -13,12 +13,14 @@ #![allow(non_camel_case_types)] use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::c_size_t; use iceoryx2::prelude::*; use iceoryx2::sample::Sample; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; +use core::ffi::c_void; use core::mem::ManuallyDrop; // BEGIN types definition @@ -98,6 +100,33 @@ impl HandleToType for iox2_sample_ref_h { // BEGIN C API +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_sample_ref_h(handle: iox2_sample_h) -> iox2_sample_ref_h { + debug_assert!(!handle.is_null()); + (*handle.as_type()).as_ref_handle() as *mut _ as _ +} + +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_payload( + sample_handle: iox2_sample_ref_h, + payload_ptr: *mut *const c_void, + payload_len: *mut c_size_t, +) { + debug_assert!(!sample_handle.is_null()); + debug_assert!(!payload_ptr.is_null()); + debug_assert!(!payload_len.is_null()); + + let sample = &mut *sample_handle.as_type(); + + let payload = match sample.service_type { + iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload(), + iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload(), + }; + + *payload_ptr = payload.as_ptr().cast(); + *payload_len = payload.len(); +} + /// This function needs to be called to destroy the sample! /// /// # Arguments From cdd0dc8be039ed86103382ea8e3a12637d1c4b18 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 19:37:46 +0200 Subject: [PATCH 12/35] [#264] Integrate cxx sample into subscriber --- .../cxx/include/iox2/enum_translation.hpp | 13 +++++++++++++ iceoryx2-ffi/cxx/include/iox2/sample.hpp | 3 +++ iceoryx2-ffi/cxx/include/iox2/subscriber.hpp | 15 ++++++++++++++- .../cxx/include/iox2/subscriber_error.hpp | 19 +++++++++++++++---- iceoryx2-ffi/ffi/src/api/quirks_correction.rs | 10 +++++++++- iceoryx2-ffi/ffi/src/api/subscriber.rs | 2 +- 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index 0e39e5748..e87fa8ca7 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -511,6 +511,19 @@ constexpr auto from(const int value) noexcept -> IOX_UNREACHABLE(); } + +template <> +constexpr auto from(const int value) noexcept -> iox2::SubscriberReceiveError { + const auto error = static_cast(value); + switch (error) { + case iox2_subscriber_receive_error_e_CONNECTION_FAILURE: + return iox2::SubscriberReceiveError::ConnectionFailure; + case iox2_subscriber_receive_error_e_EXCEEDS_MAX_BORROWED_SAMPLES: + return iox2::SubscriberReceiveError::ExceedsMaxBorrowedSamples; + } + + IOX_UNREACHABLE(); +} } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/sample.hpp b/iceoryx2-ffi/cxx/include/iox2/sample.hpp index 54f9c02f7..fcf452d20 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample.hpp @@ -40,6 +40,9 @@ class Sample { auto origin() const -> UniquePublisherId; private: + template + friend class Subscriber; + explicit Sample(iox2_sample_h handle); void drop(); diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp index 1f5e53aa7..27d860f72 100644 --- a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp @@ -96,7 +96,20 @@ inline auto Subscriber::buffer_size() const -> uint64_t template inline auto Subscriber::receive() const -> iox::expected>, SubscriberReceiveError> { - IOX_TODO(); + auto* ref_handle = iox2_cast_subscriber_ref_h(m_handle); + iox2_sample_h sample_handle {}; + auto result = iox2_subscriber_receive(ref_handle, nullptr, &sample_handle); + + if (result == IOX2_OK) { + if (sample_handle != nullptr) { + return iox::ok( + iox::optional>(Sample(sample_handle))); + } else { + return iox::ok(iox::optional>(iox::nullopt)); + } + } + + return iox::err(iox::into(result)); } template diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp index 4c3655dea..b4b4f8ce8 100644 --- a/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp @@ -16,18 +16,29 @@ #include namespace iox2 { +/// Defines the failure that can occur when receiving data with [`Subscriber::receive()`]. enum class SubscriberReceiveError : uint8_t { + /// The maximum amount of [`Sample`]s a user can borrow with [`Subscriber::receive()`] is + /// defined in [`Config`]. When this is exceeded [`Subscriber::receive()`] + /// fails. + ExceedsMaxBorrowedSamples, + + /// Occurs when a [`Subscriber`] is unable to connect to a corresponding + /// [`Publisher`]. + ConnectionFailure, }; +/// Describes the failures when a new [`Subscriber`] is created via the +/// [`PortFactorySubscriber`]. enum class SubscriberCreateError : uint8_t { /// The maximum amount of [`Subscriber`]s that can connect to a - /// [`Service`](crate::service::Service) is - /// defined in [`crate::config::Config`]. When this is exceeded no more + /// [`Service`] is defined in [`Config`]. When this is exceeded no more /// [`Subscriber`]s - /// can be created for a specific [`Service`](crate::service::Service). + /// can be created for a specific [`Service`]. ExceedsMaxSupportedSubscribers, + /// When the [`Subscriber`] requires a larger buffer size than the - /// [`Service`](crate::service::Service) offers the creation will fail. + /// [`Service`] offers the creation will fail. BufferSizeExceedsMaxSupportedBufferSizeOfService, }; } // namespace iox2 diff --git a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs index 6f22001d3..a10df1e66 100644 --- a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs +++ b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs @@ -21,7 +21,7 @@ use crate::{ iox2_notifier_create_error_e, iox2_notifier_notify_error_e, iox2_pub_sub_open_or_create_error_e, iox2_publisher_create_error_e, iox2_publisher_send_error_e, iox2_semantic_string_error_e, iox2_service_details_error_e, - iox2_subscriber_create_error_e, iox2_type_detail_error_e, + iox2_subscriber_create_error_e, iox2_subscriber_receive_error_e, iox2_type_detail_error_e, }; #[doc(hidden)] @@ -139,3 +139,11 @@ pub unsafe extern "C" fn __iox2_internal_publisher_send_error_stub() -> iox2_pub { iox2_publisher_send_error_e::CONNECTION_ERROR } + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_subscriber_receive_error_stub( +) -> iox2_subscriber_receive_error_e { + iox2_subscriber_receive_error_e::CONNECTION_FAILURE +} diff --git a/iceoryx2-ffi/ffi/src/api/subscriber.rs b/iceoryx2-ffi/ffi/src/api/subscriber.rs index 78a4f7d58..ac3499d24 100644 --- a/iceoryx2-ffi/ffi/src/api/subscriber.rs +++ b/iceoryx2-ffi/ffi/src/api/subscriber.rs @@ -183,7 +183,7 @@ pub unsafe extern "C" fn iox2_subscriber_buffer_size( /// If it is a NULL pointer, the storage will be allocated on the heap. /// * `sample_handle_ptr` - An uninitialized or dangling [`iox2_sample_h`] handle which will be initialized by this function call if a sample is obtained, otherwise it will be set to NULL. /// -/// Returns IOX2_OK on success, an [`SubscriberReceiveError`] otherwise. +/// Returns IOX2_OK on success, an [`iox2_subscriber_receive_error_e`] otherwise. /// Attention, an empty subscriber queue is not an error and even with IOX2_OK it is possible to get a NULL in `sample_handle_ptr`. /// /// # Safety From 47ca92d6fb75c3414863c1709c04604dbac5dd58 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 25 Jul 2024 19:37:46 +0200 Subject: [PATCH 13/35] [#264] Integrate cxx sample into subscriber --- examples/cxx/publish_subscribe/src/transmission_data.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cxx/publish_subscribe/src/transmission_data.hpp b/examples/cxx/publish_subscribe/src/transmission_data.hpp index 79cd37998..48ac150fa 100644 --- a/examples/cxx/publish_subscribe/src/transmission_data.hpp +++ b/examples/cxx/publish_subscribe/src/transmission_data.hpp @@ -23,7 +23,7 @@ struct TransmissionData { }; inline auto operator<<(std::ostream& stream, const TransmissionData& value) -> std::ostream& { - std::cout << "TransmissionData { x: " << value.x << ", y: " << value.y << ", funky: " << value.funky << "}"; + std::cout << "TransmissionData { x: " << value.x << ", y: " << value.y << ", funky: " << value.funky << " }"; return stream; } From 166fc5e2635f315b38443ebf86c950aefc6a80cb Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Fri, 26 Jul 2024 23:44:44 +0200 Subject: [PATCH 14/35] [#264] Add c/cxx sample_mut --- .../cxx/publish_subscribe/src/publisher.cpp | 8 +- iceoryx2-ffi/cxx/include/iox/slice.hpp | 11 + .../cxx/include/iox2/enum_translation.hpp | 17 ++ iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 12 +- iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp | 196 +++++++++------ iceoryx2-ffi/ffi/src/api/mod.rs | 2 + iceoryx2-ffi/ffi/src/api/publisher.rs | 74 +++++- iceoryx2-ffi/ffi/src/api/quirks_correction.rs | 13 +- iceoryx2-ffi/ffi/src/api/sample_mut.rs | 229 ++++++++++++++++++ iceoryx2-ffi/ffi/src/api/service_builder.rs | 2 + 10 files changed, 487 insertions(+), 77 deletions(-) create mode 100644 iceoryx2-ffi/ffi/src/api/sample_mut.rs diff --git a/examples/cxx/publish_subscribe/src/publisher.cpp b/examples/cxx/publish_subscribe/src/publisher.cpp index 087f68a69..6abfc21b8 100644 --- a/examples/cxx/publish_subscribe/src/publisher.cpp +++ b/examples/cxx/publish_subscribe/src/publisher.cpp @@ -36,13 +36,13 @@ auto main() -> int { auto counter = 0; while (node.wait(CYCLE_TIME) == NodeEvent::Tick) { counter += 1; - publisher.send_copy(TransmissionData { counter, counter * 3, counter * 812.12 }).expect("successful send"); + // publisher.send_copy(TransmissionData { counter, counter * 3, counter * 812.12 }).expect("successful send"); - // auto sample = publisher.loan_uninit().expect("acquire sample"); + auto sample = publisher.loan_uninit().expect("acquire sample"); - // sample.write_payload(TransmissionData { counter, counter * 3, counter * 812.12 }); // NOLINT + sample.write_payload(TransmissionData { counter, counter * 3, counter * 812.12 }); // NOLINT - // send_sample(std::move(sample)).expect("send successful"); + send_sample(std::move(sample)).expect("send successful"); std::cout << "Send sample " << counter << "..." << std::endl; } diff --git a/iceoryx2-ffi/cxx/include/iox/slice.hpp b/iceoryx2-ffi/cxx/include/iox/slice.hpp index 3ab02a695..c4b2f25b2 100644 --- a/iceoryx2-ffi/cxx/include/iox/slice.hpp +++ b/iceoryx2-ffi/cxx/include/iox/slice.hpp @@ -23,6 +23,7 @@ class Slice { public: using Iterator = T*; using ConstIterator = const T*; + using ValueType = T; auto size() const -> uint64_t { IOX_TODO(); @@ -46,6 +47,16 @@ class Slice { IOX_TODO(); } }; + +template +struct IsSlice { + static constexpr bool value = false; +}; + +template +struct IsSlice> { + static constexpr bool value = true; +}; } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index e87fa8ca7..3f50dde4b 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -524,6 +524,23 @@ constexpr auto from(const int value) noexcept IOX_UNREACHABLE(); } + +template <> +constexpr auto from(const int value) noexcept -> iox2::PublisherLoanError { + const auto error = static_cast(value); + switch (error) { + case iox2_publisher_loan_error_e_EXCEEDS_MAX_LOANED_SAMPLES: + return iox2::PublisherLoanError::ExceedsMaxLoanedSamples; + case iox2_publisher_loan_error_e_OUT_OF_MEMORY: + return iox2::PublisherLoanError::OutOfMemory; + case iox2_publisher_loan_error_e_EXCEEDS_MAX_LOAN_SIZE: + return iox2::PublisherLoanError::ExceedsMaxLoanSize; + case iox2_publisher_loan_error_e_INTERNAL_FAILURE: + return iox2::PublisherLoanError::InternalFailure; + } + + IOX_UNREACHABLE(); +} } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 438855a8c..45d6a0730 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -16,6 +16,7 @@ #include "iox/assertions_addendum.hpp" #include "iox/expected.hpp" #include "iox2/connection_failure.hpp" +#include "iox2/iceoryx2.h" #include "iox2/internal/iceoryx2.hpp" #include "iox2/publisher_error.hpp" #include "iox2/sample_mut.hpp" @@ -116,7 +117,16 @@ inline auto Publisher::send_copy(const Payload& payload) template inline auto Publisher::loan_uninit() -> iox::expected, PublisherLoanError> { - IOX_TODO(); + auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); + iox2_sample_mut_h sample_handle {}; + + auto result = iox2_publisher_loan(ref_handle, 1, nullptr, &sample_handle); + + if (result == IOX2_OK) { + return iox::ok(SampleMut(sample_handle)); + } + + return iox::err(iox::into(result)); } template diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index 2c0199b11..f8ec72fb5 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -13,13 +13,16 @@ #ifndef IOX2_SAMPLE_MUT_HPP #define IOX2_SAMPLE_MUT_HPP -#include "header_publish_subscribe.hpp" +#include "iox/assertions.hpp" #include "iox/assertions_addendum.hpp" #include "iox/expected.hpp" #include "iox/function.hpp" #include "iox/slice.hpp" +#include "iox2/header_publish_subscribe.hpp" +#include "iox2/iceoryx2.h" +#include "iox2/internal/iceoryx2.hpp" #include "iox2/publisher_error.hpp" -#include "service_type.hpp" +#include "iox2/service_type.hpp" #include @@ -27,89 +30,146 @@ namespace iox2 { template class SampleMut { public: - SampleMut() = default; - SampleMut(SampleMut&&) = default; - auto operator=(SampleMut&&) -> SampleMut& = default; - ~SampleMut() = default; + SampleMut(SampleMut&&) noexcept; + auto operator=(SampleMut&&) noexcept -> SampleMut&; + ~SampleMut() noexcept; SampleMut(const SampleMut&) = delete; auto operator=(const SampleMut&) -> SampleMut& = delete; - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto user_header() const -> const UserHeader& { - IOX_TODO(); - } - auto user_header_mut() -> UserHeader& { - IOX_TODO(); - } - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto payload_mut() -> Payload& { - IOX_TODO(); - } - void write_payload(const Payload& payload) { - IOX_TODO(); - } -}; + auto header() const -> const HeaderPublishSubscribe&; -template -class SampleMut { - public: - SampleMut() = default; - SampleMut(SampleMut&&) = default; - auto operator=(SampleMut&&) -> SampleMut& = default; - ~SampleMut() = default; + template , T>> + auto user_header() const -> const T&; - SampleMut(const SampleMut&) = delete; - auto operator=(const SampleMut&) -> SampleMut& = delete; + template , T>> + auto user_header_mut() -> T&; - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto payload_mut() -> Payload& { - IOX_TODO(); - } - void write_payload(const Payload& payload) { - IOX_TODO(); - } -}; + auto payload() const -> const Payload&; -template -class SampleMut, void> { - public: - SampleMut() = default; - SampleMut(SampleMut&&) = default; - auto operator=(SampleMut&&) -> SampleMut& = default; - ~SampleMut() = default; + auto payload_mut() -> Payload&; - SampleMut(const SampleMut&) = delete; - auto operator=(const SampleMut&) -> SampleMut& = delete; + template ::value, T>> + void write_payload(T&& value); - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto payload_mut() -> Payload& { - IOX_TODO(); + template ::value, T>> + void write_from_fn(const iox::function& initializer); + + protected: + template + friend class Publisher; + + template + friend auto + send_sample(SampleMut<_S, _Payload, _UserHeader>&& sample) -> iox::expected; + + explicit SampleMut(iox2_sample_mut_h handle); + void drop(); + + iox2_sample_mut_h m_handle; +}; + +template +inline SampleMut::SampleMut(iox2_sample_mut_h handle) + : m_handle { handle } { +} + +template +inline void SampleMut::drop() { + if (m_handle != nullptr) { + iox2_sample_mut_drop(m_handle); + m_handle = nullptr; } - void write_from_fn(const iox::function& initializer) { - IOX_TODO(); +} + +template +inline SampleMut::SampleMut(SampleMut&& rhs) noexcept + : m_handle { nullptr } { + *this = std::move(rhs); +} + +template +inline auto SampleMut::operator=(SampleMut&& rhs) noexcept -> SampleMut& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } -}; + + return *this; +} + +template +inline SampleMut::~SampleMut() noexcept { + drop(); +} + +template +inline auto SampleMut::header() const -> const HeaderPublishSubscribe& { + IOX_TODO(); +} + +template +template +inline auto SampleMut::user_header() const -> const T& { + IOX_TODO(); +} + +template +template +inline auto SampleMut::user_header_mut() -> T& { + IOX_TODO(); +} + +template +inline auto SampleMut::payload() const -> const Payload& { + auto* ref_handle = iox2_cast_sample_mut_ref_h(m_handle); + const void* ptr = nullptr; + size_t payload_len = 0; + + iox2_sample_mut_payload(ref_handle, &ptr, &payload_len); + IOX_ASSERT(sizeof(Payload) <= payload_len, ""); + + return *static_cast(ptr); +} + +template +inline auto SampleMut::payload_mut() -> Payload& { + auto* ref_handle = iox2_cast_sample_mut_ref_h(m_handle); + void* ptr = nullptr; + size_t payload_len = 0; + + iox2_sample_mut_payload_mut(ref_handle, &ptr, &payload_len); + IOX_ASSERT(sizeof(Payload) <= payload_len, ""); + + return *static_cast(ptr); +} template -auto send_sample(SampleMut&& sample) -> iox::expected { +template +inline void SampleMut::write_payload(T&& value) { + new (&payload_mut()) Payload(std::forward(value)); +} + +template +template +inline void +SampleMut::write_from_fn(const iox::function& initializer) { IOX_TODO(); } +template +inline auto send_sample(SampleMut&& sample) -> iox::expected { + size_t number_of_recipients = 0; + auto result = iox2_sample_mut_send(sample.m_handle, &number_of_recipients); + + if (result == IOX2_OK) { + return iox::ok(number_of_recipients); + } + + return iox::err(iox::into(result)); +} + } // namespace iox2 #endif diff --git a/iceoryx2-ffi/ffi/src/api/mod.rs b/iceoryx2-ffi/ffi/src/api/mod.rs index d37948fdd..643cabc98 100644 --- a/iceoryx2-ffi/ffi/src/api/mod.rs +++ b/iceoryx2-ffi/ffi/src/api/mod.rs @@ -34,6 +34,7 @@ mod port_factory_subscriber_builder; mod publisher; mod quirks_correction; mod sample; +mod sample_mut; mod service; mod service_builder; mod service_builder_event; @@ -58,6 +59,7 @@ pub use port_factory_subscriber_builder::*; pub use publisher::*; pub use quirks_correction::*; pub use sample::*; +pub use sample_mut::*; pub use service::*; pub use service_builder::*; pub use service_builder_event::*; diff --git a/iceoryx2-ffi/ffi/src/api/publisher.rs b/iceoryx2-ffi/ffi/src/api/publisher.rs index 185a3bb70..bf29737d8 100644 --- a/iceoryx2-ffi/ffi/src/api/publisher.rs +++ b/iceoryx2-ffi/ffi/src/api/publisher.rs @@ -12,7 +12,7 @@ #![allow(non_camel_case_types)] -use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi, SampleMutUnion}; use crate::IOX2_OK; use iceoryx2::port::publisher::{Publisher, PublisherLoanError, PublisherSendError}; @@ -82,6 +82,15 @@ impl IntoCInt for PublisherLoanError { } } +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_publisher_loan_error_e { + OUT_OF_MEMORY = IOX2_OK as isize + 1, + EXCEEDS_MAX_LOANED_SAMPLES, + EXCEEDS_MAX_LOAN_SIZE, + INTERNAL_FAILURE, +} + pub(super) union PublisherUnion { ipc: ManuallyDrop>, local: ManuallyDrop>, @@ -229,6 +238,67 @@ pub unsafe extern "C" fn iox2_publisher_send_copy( } } +#[no_mangle] +pub unsafe extern "C" fn iox2_publisher_loan( + publisher_handle: iox2_publisher_ref_h, + number_of_elements: c_size_t, + sample_struct_ptr: *mut iox2_sample_mut_t, + sample_handle_ptr: *mut iox2_sample_mut_h, +) -> c_int { + debug_assert!(!publisher_handle.is_null()); + debug_assert!(!sample_handle_ptr.is_null()); + + *sample_handle_ptr = std::ptr::null_mut(); + + let mut sample_struct_ptr = sample_struct_ptr; + fn no_op(_: *mut iox2_sample_mut_t) {} + let mut deleter: fn(*mut iox2_sample_mut_t) = no_op; + if sample_struct_ptr.is_null() { + sample_struct_ptr = iox2_sample_mut_t::alloc(); + deleter = iox2_sample_mut_t::dealloc; + } + debug_assert!(!sample_struct_ptr.is_null()); + + let publisher = &mut *publisher_handle.as_type(); + + match publisher.service_type { + iox2_service_type_e::IPC => match publisher + .value + .as_ref() + .ipc + .loan_slice_uninit(number_of_elements) + { + Ok(sample) => { + (*sample_struct_ptr).init( + publisher.service_type, + SampleMutUnion::new_ipc(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + IOX2_OK + } + Err(error) => error.into_c_int(), + }, + iox2_service_type_e::LOCAL => match publisher + .value + .as_ref() + .local + .loan_slice_uninit(number_of_elements) + { + Ok(sample) => { + (*sample_struct_ptr).init( + publisher.service_type, + SampleMutUnion::new_local(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + IOX2_OK + } + Err(error) => error.into_c_int(), + }, + } +} + /// This function needs to be called to destroy the publisher! /// /// # Arguments @@ -262,7 +332,7 @@ pub unsafe extern "C" fn iox2_publisher_drop(publisher_handle: iox2_publisher_h) use core::time::Duration; use iceoryx2_bb_log::set_log_level; -use super::IntoCInt; +use super::{c_size_t, iox2_sample_mut_h, iox2_sample_mut_t, IntoCInt}; const CYCLE_TIME: Duration = Duration::from_secs(1); diff --git a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs index a10df1e66..a0eb0e823 100644 --- a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs +++ b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs @@ -20,8 +20,9 @@ use crate::{ iox2_node_creation_failure_e, iox2_node_event_e, iox2_node_list_failure_e, iox2_notifier_create_error_e, iox2_notifier_notify_error_e, iox2_pub_sub_open_or_create_error_e, iox2_publisher_create_error_e, - iox2_publisher_send_error_e, iox2_semantic_string_error_e, iox2_service_details_error_e, - iox2_subscriber_create_error_e, iox2_subscriber_receive_error_e, iox2_type_detail_error_e, + iox2_publisher_loan_error_e, iox2_publisher_send_error_e, iox2_semantic_string_error_e, + iox2_service_details_error_e, iox2_subscriber_create_error_e, iox2_subscriber_receive_error_e, + iox2_type_detail_error_e, }; #[doc(hidden)] @@ -140,6 +141,14 @@ pub unsafe extern "C" fn __iox2_internal_publisher_send_error_stub() -> iox2_pub iox2_publisher_send_error_e::CONNECTION_ERROR } +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_publisher_loan_error_stub() -> iox2_publisher_loan_error_e +{ + iox2_publisher_loan_error_e::INTERNAL_FAILURE +} + #[doc(hidden)] #[no_mangle] // TODO: enums are only exported when they are actually used by some function diff --git a/iceoryx2-ffi/ffi/src/api/sample_mut.rs b/iceoryx2-ffi/ffi/src/api/sample_mut.rs new file mode 100644 index 000000000..be09d5469 --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/sample_mut.rs @@ -0,0 +1,229 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, IntoCInt, NoUserHeaderFfi}; +use crate::{c_size_t, IOX2_OK}; + +use iceoryx2::prelude::*; +use iceoryx2::sample_mut::SampleMut; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::ffi::{c_int, c_void}; +use core::mem::ManuallyDrop; + +use super::UninitPayloadFfi; + +// BEGIN types definition + +pub(super) union SampleMutUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl SampleMutUnion { + pub(super) fn new_ipc( + sample: SampleMut, + ) -> Self { + Self { + ipc: ManuallyDrop::new(sample), + } + } + pub(super) fn new_local( + sample: SampleMut, + ) -> Self { + Self { + local: ManuallyDrop::new(sample), + } + } +} + +#[repr(C)] +#[repr(align(8))] // alignment of Option +pub struct iox2_sample_mut_storage_t { + internal: [u8; 56], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(SampleMutUnion)] +pub struct iox2_sample_mut_t { + service_type: iox2_service_type_e, + value: iox2_sample_mut_storage_t, + deleter: fn(*mut iox2_sample_mut_t), +} + +impl iox2_sample_mut_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: SampleMutUnion, + deleter: fn(*mut iox2_sample_mut_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_sample_mut_h_t; +/// The owning handle for `iox2_sample_mut_t`. Passing the handle to an function transfers the ownership. +pub type iox2_sample_mut_h = *mut iox2_sample_mut_h_t; + +pub struct iox2_sample_mut_ref_h_t; +/// The non-owning handle for `iox2_sample_mut_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_sample_mut_ref_h = *mut iox2_sample_mut_ref_h_t; + +impl HandleToType for iox2_sample_mut_h { + type Target = *mut iox2_sample_mut_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_sample_mut_ref_h { + type Target = *mut iox2_sample_mut_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_sample_mut_ref_h( + handle: iox2_sample_mut_h, +) -> iox2_sample_mut_ref_h { + debug_assert!(!handle.is_null()); + (*handle.as_type()).as_ref_handle() as *mut _ as _ +} + +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_payload_mut( + sample_handle: iox2_sample_mut_ref_h, + payload_ptr: *mut *mut c_void, + payload_len: *mut c_size_t, +) { + debug_assert!(!sample_handle.is_null()); + debug_assert!(!payload_ptr.is_null()); + debug_assert!(!payload_len.is_null()); + + let sample = &mut *sample_handle.as_type(); + + let payload = match sample.service_type { + iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload_mut(), + iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload_mut(), + }; + + *payload_ptr = payload.as_mut_ptr().cast(); + *payload_len = payload.len(); +} + +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_payload( + sample_handle: iox2_sample_mut_ref_h, + payload_ptr: *mut *const c_void, + payload_len: *mut c_size_t, +) { + debug_assert!(!sample_handle.is_null()); + debug_assert!(!payload_ptr.is_null()); + debug_assert!(!payload_len.is_null()); + + let sample = &mut *sample_handle.as_type(); + + let payload = match sample.service_type { + iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload(), + iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload(), + }; + + *payload_ptr = payload.as_ptr().cast(); + *payload_len = payload.len(); +} + +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_send( + sample_handle: iox2_sample_mut_h, + number_of_recipients: *mut c_size_t, +) -> c_int { + debug_assert!(!sample_handle.is_null()); + debug_assert!(!number_of_recipients.is_null()); + + let sample = &mut *sample_handle.as_type(); + + match sample.service_type { + iox2_service_type_e::IPC => { + match ManuallyDrop::take(&mut sample.value.as_mut().ipc) + .assume_init() + .send() + { + Ok(v) => { + *number_of_recipients = v; + IOX2_OK + } + Err(e) => { + (sample.deleter)(sample); + e.into_c_int() + } + } + } + iox2_service_type_e::LOCAL => { + match ManuallyDrop::take(&mut sample.value.as_mut().local) + .assume_init() + .send() + { + Ok(v) => { + *number_of_recipients = v; + IOX2_OK + } + Err(e) => { + (sample.deleter)(sample); + e.into_c_int() + } + } + } + } +} + +/// This function needs to be called to destroy the sample! +/// +/// # Arguments +/// +/// * `sample_handle` - A valid [`iox2_sample_mut_h`] +/// +/// # Safety +/// +/// * The `sample_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_sample_mut_t`] can be re-used with a call to +/// [`iox2_subscriber_receive`](crate::iox2_subscriber_receive)! +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_drop(sample_handle: iox2_sample_mut_h) { + debug_assert!(!sample_handle.is_null()); + + let sample = &mut *sample_handle.as_type(); + + match sample.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut sample.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut sample.value.as_mut().local); + } + } + (sample.deleter)(sample); +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/service_builder.rs b/iceoryx2-ffi/ffi/src/api/service_builder.rs index 63a1a1f1f..bdb457e1d 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder.rs @@ -23,12 +23,14 @@ use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; use core::mem::ManuallyDrop; +use core::mem::MaybeUninit; // BEGIN types definition pub(super) type NoUserHeaderFfi = (); pub(super) type _UserHeaderFfi = [u8; 128]; pub(super) type PayloadFfi = [u8]; +pub(super) type UninitPayloadFfi = [MaybeUninit]; pub(super) union ServiceBuilderUnionNested { pub(super) base: ManuallyDrop>, From d8b7f89935a21f8438d71ac46cdaadcd6951e4e1 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 00:23:18 +0200 Subject: [PATCH 15/35] [#264] Add cxx documentation --- examples/README.md | 2 +- .../cxx/publish_subscribe/src/publisher.cpp | 1 - .../include/iox2/port_factory_publisher.hpp | 17 ++++++ .../include/iox2/port_factory_subscriber.hpp | 4 ++ iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 34 +++++++++++ .../cxx/include/iox2/publisher_error.hpp | 12 ++-- iceoryx2-ffi/cxx/include/iox2/sample.hpp | 21 +++++++ iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp | 58 +++++++++++++++++++ iceoryx2-ffi/cxx/include/iox2/subscriber.hpp | 14 +++++ 9 files changed, 156 insertions(+), 7 deletions(-) diff --git a/examples/README.md b/examples/README.md index 612af6194..41913404c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -49,7 +49,7 @@ they interact and exchange data. | discovery | [Rust](rust/discovery) | List all available services in a system. | | docker | [all](rust/docker) | Communicate between different docker containers and the host. | | event | [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them.| -| publish subscribe | [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | +| publish subscribe | [C++](rust/publish_subscribe) [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | | publish subscribe dynamic data | [Rust](rust/publish_subscribe_dynamic_data) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) and payload data that has a dynamic size. | | publish subscribe with user header | [Rust](rust/publish_subscribe_with_user_header) | Add a user header to the payload (samples) to transfer additional information. | | service attributes | [Rust](rust/service_attributes) | Creates a service with custom attributes that are available to every endpoint. If the attributes are not compatible the service will not open. | diff --git a/examples/cxx/publish_subscribe/src/publisher.cpp b/examples/cxx/publish_subscribe/src/publisher.cpp index 6abfc21b8..8a0993091 100644 --- a/examples/cxx/publish_subscribe/src/publisher.cpp +++ b/examples/cxx/publish_subscribe/src/publisher.cpp @@ -36,7 +36,6 @@ auto main() -> int { auto counter = 0; while (node.wait(CYCLE_TIME) == NodeEvent::Tick) { counter += 1; - // publisher.send_copy(TransmissionData { counter, counter * 3, counter * 812.12 }).expect("successful send"); auto sample = publisher.loan_uninit().expect("acquire sample"); diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp index 8e85a1dff..4716adfa8 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp @@ -23,15 +23,31 @@ #include namespace iox2 { +/// Defines the strategy the [`Publisher`] shall pursue in +/// [`send_sample(`] or +/// [`Publisher::send_copy()`] when the buffer of a +/// [`Subscriber`] is full and the service does not overflow. enum class UnableToDeliverStrategy : uint8_t { + /// Blocks until the [`Subscriber`] has consumed the + /// [`Sample`] from the buffer and there is space again Block, + /// Do not deliver the [`Sample`]. DiscardSample }; +/// Factory to create a new [`Publisher`] port/endpoint for +/// [`MessagingPattern::PublishSubscribe`] based communication. template class PortFactoryPublisher { + /// Sets the [`UnableToDeliverStrategy`]. IOX_BUILDER_OPTIONAL(UnableToDeliverStrategy, unable_to_deliver_strategy); + + /// Defines how many [`SampleMut`] the [`Publisher`] can loan with + /// [`Publisher::loan()`] or [`Publisher::loan_uninit()`] in parallel. IOX_BUILDER_OPTIONAL(uint64_t, max_loaned_samples); + + /// Sets the maximum slice length that a user can allocate with + /// [`Publisher::loan_slice()`] or [`Publisher::loan_slice_uninit()`]. IOX_BUILDER_OPTIONAL(uint64_t, max_slice_len); public: @@ -41,6 +57,7 @@ class PortFactoryPublisher { auto operator=(PortFactoryPublisher&&) -> PortFactoryPublisher& = default; ~PortFactoryPublisher() = default; + /// Creates a new [`Publisher`] or returns a [`PublisherCreateError`] on failure. auto create() && -> iox::expected, PublisherCreateError>; private: diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp index c55484937..26d2d074f 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp @@ -24,8 +24,11 @@ namespace iox2 { +/// Factory to create a new [`Subscriber`] port/endpoint for +/// [`MessagingPattern::PublishSubscribe`] based communication. template class PortFactorySubscriber { + /// Defines the required buffer size of the [`Subscriber`]. Smallest possible value is `1`. IOX_BUILDER_OPTIONAL(uint64_t, buffer_size); public: @@ -35,6 +38,7 @@ class PortFactorySubscriber { auto operator=(PortFactorySubscriber&&) -> PortFactorySubscriber& = default; ~PortFactorySubscriber() = default; + /// Creates a new [`Subscriber`] or returns a [`SubscriberCreateError`] on failure. auto create() && -> iox::expected, SubscriberCreateError>; private: diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 45d6a0730..13b112e90 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -26,6 +26,7 @@ #include namespace iox2 { +/// Sending endpoint of a publish-subscriber based communication. template class Publisher { public: @@ -36,16 +37,49 @@ class Publisher { Publisher(const Publisher&) = delete; auto operator=(const Publisher&) -> Publisher& = delete; + /// Returns the [`UniquePublisherId`] of the [`Publisher`] auto id() const -> UniquePublisherId; + + /// Copies the input `value` into a [`SampleMut`] and delivers it. + /// On success it returns the number of [`Subscriber`]s that received + /// the data, otherwise a [`PublisherSendError`] describing the failure. auto send_copy(const Payload& payload) const -> iox::expected; + + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`]. + /// The user has to initialize the payload before it can be sent. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. auto loan_uninit() -> iox::expected, PublisherLoanError>; + + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`] + /// and initialize it with the default value. This can be a performance hit and [`Publisher::loan_uninit`] + /// can be used to loan an uninitalized [`SampleMut`]. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. auto loan() -> iox::expected, PublisherLoanError>; + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`] + /// and initializes all slice elements with the default value. This can be a performance hit + /// and [`Publisher::loan_slice_uninit()`] can be used to loan a slice of uninitialized + /// [`Payload`]. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. auto loan_slice(const uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; + + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`]. + /// The user has to initialize the payload before it can be sent. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. auto loan_slice_uninit(const uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; + /// Explicitly updates all connections to the [`Subscriber`]s. This is + /// required to be called whenever a new [`Subscriber`] is connected to + /// the service. It is called implicitly whenever [`SampleMut::send()`] or + /// [`Publisher::send_copy()`] is called. + /// When a [`Subscriber`] is connected that requires a history this + /// call will deliver it. auto update_connections() -> iox::expected; private: diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp index 2e65e9444..27118a78d 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp @@ -17,12 +17,14 @@ namespace iox2 { +/// Defines a failure that can occur when a [`Publisher`] is created with +/// [`PortFactoryPublisher`]. enum class PublisherCreateError : uint8_t { /// The maximum amount of [`Publisher`]s that can connect to a - /// [`Service`](crate::service::Service) is - /// defined in [`crate::config::Config`]. When this is exceeded no more + /// [`Service`] is + /// defined in [`Config`]. When this is exceeded no more /// [`Publisher`]s - /// can be created for a specific [`Service`](crate::service::Service). + /// can be created for a specific [`Service`]. ExceedsMaxSupportedPublishers, /// The datasegment in which the payload of the [`Publisher`] is stored, /// could not be created. @@ -38,7 +40,7 @@ enum class PublisherLoanError : uint8_t { /// The maximum amount of [`SampleMut`]s a user can borrow with /// [`Publisher::loan()`] or /// [`Publisher::loan_uninit()`] is - /// defined in [`crate::config::Config`]. When this is exceeded those calls + /// defined in [`Config`]. When this is exceeded those calls /// will fail. ExceedsMaxLoanedSamples, /// The provided slice size exceeds the configured max slice size of the @@ -46,7 +48,7 @@ enum class PublisherLoanError : uint8_t { /// To send a [`SampleMut`] with this size a new [`Publisher`] has to be /// created with /// a - /// [`crate::service::port_factory::publisher::PortFactoryPublisher::max_slice_len()`] + /// [`PortFactoryPublisher::max_slice_len()`] /// greater or equal to the required len. ExceedsMaxLoanSize, /// Errors that indicate either an implementation issue or a wrongly diff --git a/iceoryx2-ffi/cxx/include/iox2/sample.hpp b/iceoryx2-ffi/cxx/include/iox2/sample.hpp index fcf452d20..c203fbd84 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample.hpp @@ -20,6 +20,17 @@ #include "iox2/unique_port_id.hpp" namespace iox2 { +/// It stores the payload and is acquired by the [`Subscriber`] whenever +/// it receives new data from a [`Publisher`] via +/// [`Subscriber::receive()`]. +/// # Notes +/// +/// Does not implement [`Send`] since it releases unsent samples vie the [`Subscriber`] and the +/// [`Subscriber`] is not thread-safe! +/// +/// # Important +/// +/// DO NOT MOVE THE SAMPLE INTO ANOTHER THREAD! template class Sample { public: @@ -30,13 +41,23 @@ class Sample { Sample(const Sample&) = delete; auto operator=(const Sample&) -> Sample& = delete; + /// Returns a reference to the payload of the [`Sample`] auto operator*() const -> const Payload&; + + /// Returns a pointer to the payload of the [`Sample`] auto operator->() const -> const Payload*; + /// Returns a reference to the payload of the [`Sample`] auto payload() const -> const Payload&; + + /// Returns a reference to the user_header of the [`Sample`] template , T>> auto user_header() const -> const T&; + + /// Returns a reference to the [`Header`] of the [`Sample`]. auto header() const -> const HeaderPublishSubscribe&; + + /// Returns the [`UniquePublisherId`] of the [`Publisher`](crate::port::publisher::Publisher) auto origin() const -> UniquePublisherId; private: diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index f8ec72fb5..f3127a5d9 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -27,6 +27,25 @@ #include namespace iox2 { + +/// Acquired by a [`Publisher`] via +/// * [`Publisher::loan()`], +/// * [`Publisher::loan_uninit()`] +/// * [`Publisher::loan_slice()`] +/// * [`Publisher::loan_slice_uninit()`] +/// +/// It stores the payload that will be sent +/// to all connected [`Subscriber`]s. If the [`SampleMut`] is not sent +/// it will release the loaned memory when going out of scope. +/// +/// # Notes +/// +/// Does not implement [`Send`] since it releases unsent samples in the [`Publisher`] and the +/// [`Publisher`] is not thread-safe! +/// +/// # Important +/// +/// DO NOT MOVE THE SAMPLE INTO ANOTHER THREAD! template class SampleMut { public: @@ -37,21 +56,40 @@ class SampleMut { SampleMut(const SampleMut&) = delete; auto operator=(const SampleMut&) -> SampleMut& = delete; + /// Returns a const reference to the payload of the [`Sample`] + auto operator*() const -> const Payload&; + + /// Returns a reference to the payload of the [`Sample`] + auto operator*() -> Payload&; + + /// Returns a const pointer to the payload of the [`Sample`] + auto operator->() const -> const Payload*; + + /// Returns a pointer to the payload of the [`Sample`] + auto operator->() -> Payload*; + + /// Returns a reference to the [`Header`] of the [`Sample`]. auto header() const -> const HeaderPublishSubscribe&; + /// Returns a reference to the user_header of the [`Sample`] template , T>> auto user_header() const -> const T&; + /// Returns a mutable reference to the user_header of the [`Sample`]. template , T>> auto user_header_mut() -> T&; + /// Returns a reference to the const payload of the sample. auto payload() const -> const Payload&; + /// Returns a reference to the payload of the sample. auto payload_mut() -> Payload&; + /// Writes the payload to the sample template ::value, T>> void write_payload(T&& value); + /// Writes the payload to the sample template ::value, T>> void write_from_fn(const iox::function& initializer); @@ -104,6 +142,26 @@ inline SampleMut::~SampleMut() noexcept { drop(); } +template +inline auto SampleMut::operator*() const -> const Payload& { + return payload(); +} + +template +inline auto SampleMut::operator*() -> Payload& { + return payload_mut(); +} + +template +inline auto SampleMut::operator->() const -> const Payload* { + return &payload(); +} + +template +inline auto SampleMut::operator->() -> Payload* { + return &payload_mut(); +} + template inline auto SampleMut::header() const -> const HeaderPublishSubscribe& { IOX_TODO(); diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp index 27d860f72..6ca64bde2 100644 --- a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp @@ -24,6 +24,7 @@ #include "iox2/unique_port_id.hpp" namespace iox2 { +/// The receiving endpoint of a publish-subscribe communication. template class Subscriber { public: @@ -34,9 +35,22 @@ class Subscriber { Subscriber(const Subscriber&) = delete; auto operator=(const Subscriber&) -> Subscriber& = delete; + /// Returns the [`UniqueSubscriberId`] of the [`Subscriber`] auto id() const -> UniqueSubscriberId; + + /// Returns the internal buffer size of the [`Subscriber`]. auto buffer_size() const -> uint64_t; + + /// Receives a [`Sample`] from [`Publisher`]. If no sample could be + /// received [`None`] is returned. If a failure occurs [`SubscriberReceiveError`] is returned. auto receive() const -> iox::expected>, SubscriberReceiveError>; + + /// Explicitly updates all connections to the [`Subscriber`]s. This is + /// required to be called whenever a new [`Subscriber`] connected to + /// the service. It is done implicitly whenever [`SampleMut::send()`] or + /// [`Publisher::send_copy()`] is called. + /// When a [`Subscriber`] is connected that requires a history this + /// call will deliver it. auto update_connections() const -> iox::expected; private: From bb0de5ec179c89b5081ff54671e603c11d79506d Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Sat, 27 Jul 2024 00:51:09 +0200 Subject: [PATCH 16/35] [#264] Fix dropping of dangling sample --- iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp | 1 + iceoryx2-ffi/ffi/src/api/sample_mut.rs | 36 +++++++++++--------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index f3127a5d9..a2401e5d9 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -220,6 +220,7 @@ template inline auto send_sample(SampleMut&& sample) -> iox::expected { size_t number_of_recipients = 0; auto result = iox2_sample_mut_send(sample.m_handle, &number_of_recipients); + sample.m_handle = nullptr; if (result == IOX2_OK) { return iox::ok(number_of_recipients); diff --git a/iceoryx2-ffi/ffi/src/api/sample_mut.rs b/iceoryx2-ffi/ffi/src/api/sample_mut.rs index be09d5469..d68199e68 100644 --- a/iceoryx2-ffi/ffi/src/api/sample_mut.rs +++ b/iceoryx2-ffi/ffi/src/api/sample_mut.rs @@ -162,40 +162,44 @@ pub unsafe extern "C" fn iox2_sample_mut_send( debug_assert!(!sample_handle.is_null()); debug_assert!(!number_of_recipients.is_null()); - let sample = &mut *sample_handle.as_type(); + let sample_struct = &mut *sample_handle.as_type(); + let service_type = sample_struct.service_type; - match sample.service_type { + let sample = sample_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| panic!("Trying to send an already sent sample!")); + (sample_struct.deleter)(sample_struct); + + match service_type { iox2_service_type_e::IPC => { - match ManuallyDrop::take(&mut sample.value.as_mut().ipc) - .assume_init() - .send() - { + let sample = ManuallyDrop::into_inner(sample.ipc); + match sample.assume_init().send() { Ok(v) => { *number_of_recipients = v; - IOX2_OK } Err(e) => { - (sample.deleter)(sample); - e.into_c_int() + (sample_struct.deleter)(sample_struct); + return e.into_c_int(); } } } iox2_service_type_e::LOCAL => { - match ManuallyDrop::take(&mut sample.value.as_mut().local) - .assume_init() - .send() - { + let sample = ManuallyDrop::into_inner(sample.local); + match sample.assume_init().send() { Ok(v) => { *number_of_recipients = v; - IOX2_OK } Err(e) => { - (sample.deleter)(sample); - e.into_c_int() + (sample_struct.deleter)(sample_struct); + return e.into_c_int(); } } } } + + IOX2_OK } /// This function needs to be called to destroy the sample! From 7c0f801f6a73fabca73c73ac9140a58b9ec7dc66 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Sat, 27 Jul 2024 01:15:52 +0200 Subject: [PATCH 17/35] [#264] Fix memory leak in service_builder C API --- .../ffi/src/api/service_builder_event.rs | 90 +++++++++------- .../ffi/src/api/service_builder_pub_sub.rs | 102 ++++++++++-------- 2 files changed, 108 insertions(+), 84 deletions(-) diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs index 26081d16b..0c6817fa5 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs @@ -150,24 +150,24 @@ pub unsafe extern "C" fn iox2_service_builder_event_set_max_notifiers( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_event( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event( service_builder.max_notifiers(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_local_event( + service_builder_struct.set(ServiceBuilderUnion::new_local_event( service_builder.max_notifiers(value), )); } @@ -193,24 +193,24 @@ pub unsafe extern "C" fn iox2_service_builder_event_set_max_listeners( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_event( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event( service_builder.max_listeners(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_local_event( + service_builder_struct.set(ServiceBuilderUnion::new_local_event( service_builder.max_listeners(value), )); } @@ -245,8 +245,6 @@ pub unsafe extern "C" fn iox2_service_builder_event_open_or_create( debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - let mut port_factory_struct_ptr = port_factory_struct_ptr; fn no_op(_: *mut iox2_port_factory_event_t) {} let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; @@ -256,12 +254,20 @@ pub unsafe extern "C" fn iox2_service_builder_event_open_or_create( } debug_assert!(!port_factory_struct_ptr.is_null()); - let service_type = service_builders_struct.service_type; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_event_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); match service_builder.open_or_create() { @@ -278,9 +284,7 @@ pub unsafe extern "C" fn iox2_service_builder_event_open_or_create( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.event); match service_builder.open_or_create() { @@ -329,8 +333,6 @@ pub unsafe extern "C" fn iox2_service_builder_event_open( debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - let mut port_factory_struct_ptr = port_factory_struct_ptr; fn no_op(_: *mut iox2_port_factory_event_t) {} let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; @@ -340,12 +342,20 @@ pub unsafe extern "C" fn iox2_service_builder_event_open( } debug_assert!(!port_factory_struct_ptr.is_null()); - let service_type = service_builders_struct.service_type; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_event_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); match service_builder.open() { @@ -362,9 +372,7 @@ pub unsafe extern "C" fn iox2_service_builder_event_open( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.event); match service_builder.open() { @@ -413,8 +421,6 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - let mut port_factory_struct_ptr = port_factory_struct_ptr; fn no_op(_: *mut iox2_port_factory_event_t) {} let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; @@ -424,12 +430,20 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( } debug_assert!(!port_factory_struct_ptr.is_null()); - let service_type = service_builders_struct.service_type; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_event_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); match service_builder.create() { @@ -446,9 +460,7 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.event); match service_builder.create() { diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs index becb2c134..cbd14f565 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs @@ -238,24 +238,24 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_set_payload_type_details( alignment, }; - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( service_builder.__internal_set_payload_type_details(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_local_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_local_pub_sub( service_builder.__internal_set_payload_type_details(value), )); } @@ -283,24 +283,24 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_set_max_publishers( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( service_builder.max_publishers(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_local_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_local_pub_sub( service_builder.max_publishers(value), )); } @@ -326,24 +326,24 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_set_max_subscribers( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( service_builder.max_subscribers(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_local_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_local_pub_sub( service_builder.max_subscribers(value), )); } @@ -378,8 +378,6 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open_or_create( debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - let mut port_factory_struct_ptr = port_factory_struct_ptr; fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; @@ -389,12 +387,20 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open_or_create( } debug_assert!(!port_factory_struct_ptr.is_null()); - let service_type = service_builders_struct.service_type; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_pub_sub_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); match service_builder.open_or_create() { @@ -411,9 +417,7 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open_or_create( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); match service_builder.open_or_create() { @@ -462,8 +466,6 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open( debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - let mut port_factory_struct_ptr = port_factory_struct_ptr; fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; @@ -473,12 +475,20 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open( } debug_assert!(!port_factory_struct_ptr.is_null()); - let service_type = service_builders_struct.service_type; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_pub_sub_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); match service_builder.open() { @@ -495,9 +505,7 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); match service_builder.open() { @@ -546,8 +554,6 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - let mut port_factory_struct_ptr = port_factory_struct_ptr; fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; @@ -557,12 +563,20 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( } debug_assert!(!port_factory_struct_ptr.is_null()); - let service_type = service_builders_struct.service_type; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_pub_sub_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); match service_builder.create() { @@ -579,9 +593,7 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); match service_builder.create() { From 9e60a9da6715fa39e9079b4685d11f0dcb95f38c Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Sat, 27 Jul 2024 01:27:36 +0200 Subject: [PATCH 18/35] [#264] Fix memory leak in port_factory C API --- .../ffi/src/api/port_factory_listener_builder.rs | 16 +++++++++++----- .../ffi/src/api/port_factory_notifier_builder.rs | 16 +++++++++++----- .../src/api/port_factory_publisher_builder.rs | 16 +++++++++++----- .../src/api/port_factory_subscriber_builder.rs | 16 +++++++++++----- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs index d82f8b01f..53eeb130b 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs @@ -159,12 +159,19 @@ pub unsafe extern "C" fn iox2_port_factory_listener_builder_create( debug_assert!(!listener_struct_ptr.is_null()); let listener_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; - let service_type = listener_builder_struct.service_type; + let listener_builder = listener_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_listener_builder_h'!") + }); + (listener_builder_struct.deleter)(listener_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let listener_builder = - ManuallyDrop::take(&mut listener_builder_struct.value.as_mut().ipc); + let listener_builder = ManuallyDrop::into_inner(listener_builder.ipc); match listener_builder.create() { Ok(listener) => { @@ -180,8 +187,7 @@ pub unsafe extern "C" fn iox2_port_factory_listener_builder_create( } } iox2_service_type_e::LOCAL => { - let listener_builder = - ManuallyDrop::take(&mut listener_builder_struct.value.as_mut().local); + let listener_builder = ManuallyDrop::into_inner(listener_builder.local); match listener_builder.create() { Ok(listener) => { diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs index d9390e589..3cf64246b 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs @@ -215,12 +215,19 @@ pub unsafe extern "C" fn iox2_port_factory_notifier_builder_create( debug_assert!(!notifier_struct_ptr.is_null()); let notifier_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; - let service_type = notifier_builder_struct.service_type; + let notifier_builder = notifier_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_notifier_builder_h'!") + }); + (notifier_builder_struct.deleter)(notifier_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let notifier_builder = - ManuallyDrop::take(&mut notifier_builder_struct.value.as_mut().ipc); + let notifier_builder = ManuallyDrop::into_inner(notifier_builder.ipc); match notifier_builder.create() { Ok(notifier) => { @@ -236,8 +243,7 @@ pub unsafe extern "C" fn iox2_port_factory_notifier_builder_create( } } iox2_service_type_e::LOCAL => { - let notifier_builder = - ManuallyDrop::take(&mut notifier_builder_struct.value.as_mut().local); + let notifier_builder = ManuallyDrop::into_inner(notifier_builder.local); match notifier_builder.create() { Ok(notifier) => { diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs index 6905be0d7..1c24adb45 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs @@ -235,12 +235,19 @@ pub unsafe extern "C" fn iox2_port_factory_publisher_builder_create( debug_assert!(!publisher_struct_ptr.is_null()); let publisher_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; - let service_type = publisher_builder_struct.service_type; + let publisher_builder = publisher_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_publisher_builder_h'!") + }); + (publisher_builder_struct.deleter)(publisher_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let publisher_builder = - ManuallyDrop::take(&mut publisher_builder_struct.value.as_mut().ipc); + let publisher_builder = ManuallyDrop::into_inner(publisher_builder.ipc); match publisher_builder.create() { Ok(publisher) => { @@ -256,8 +263,7 @@ pub unsafe extern "C" fn iox2_port_factory_publisher_builder_create( } } iox2_service_type_e::LOCAL => { - let publisher_builder = - ManuallyDrop::take(&mut publisher_builder_struct.value.as_mut().local); + let publisher_builder = ManuallyDrop::into_inner(publisher_builder.local); match publisher_builder.create() { Ok(publisher) => { diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs index 5b80f5cb6..ee097fd6a 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs @@ -235,12 +235,19 @@ pub unsafe extern "C" fn iox2_port_factory_subscriber_builder_create( debug_assert!(!subscriber_struct_ptr.is_null()); let subscriber_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; - let service_type = subscriber_builder_struct.service_type; + let subscriber_builder = subscriber_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_subscriber_builder_h'!") + }); + (subscriber_builder_struct.deleter)(subscriber_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let subscriber_builder = - ManuallyDrop::take(&mut subscriber_builder_struct.value.as_mut().ipc); + let subscriber_builder = ManuallyDrop::into_inner(subscriber_builder.ipc); match subscriber_builder.create() { Ok(subscriber) => { @@ -256,8 +263,7 @@ pub unsafe extern "C" fn iox2_port_factory_subscriber_builder_create( } } iox2_service_type_e::LOCAL => { - let subscriber_builder = - ManuallyDrop::take(&mut subscriber_builder_struct.value.as_mut().local); + let subscriber_builder = ManuallyDrop::into_inner(subscriber_builder.local); match subscriber_builder.create() { Ok(subscriber) => { From d393bb69157cd9a20ed0b9f307379e3bf0e998ea Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Sat, 27 Jul 2024 01:37:30 +0200 Subject: [PATCH 19/35] [#264] Fix memory leak in subscriber C API --- iceoryx2-ffi/ffi/src/api/subscriber.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/iceoryx2-ffi/ffi/src/api/subscriber.rs b/iceoryx2-ffi/ffi/src/api/subscriber.rs index ac3499d24..a32b2cd5c 100644 --- a/iceoryx2-ffi/ffi/src/api/subscriber.rs +++ b/iceoryx2-ffi/ffi/src/api/subscriber.rs @@ -200,20 +200,25 @@ pub unsafe extern "C" fn iox2_subscriber_receive( *sample_handle_ptr = std::ptr::null_mut(); - let mut sample_struct_ptr = sample_struct_ptr; - fn no_op(_: *mut iox2_sample_t) {} - let mut deleter: fn(*mut iox2_sample_t) = no_op; - if sample_struct_ptr.is_null() { - sample_struct_ptr = iox2_sample_t::alloc(); - deleter = iox2_sample_t::dealloc; - } - debug_assert!(!sample_struct_ptr.is_null()); + let init_sample_struct_ptr = |sample_struct_ptr: *mut iox2_sample_t| { + let mut sample_struct_ptr = sample_struct_ptr; + fn no_op(_: *mut iox2_sample_t) {} + let mut deleter: fn(*mut iox2_sample_t) = no_op; + if sample_struct_ptr.is_null() { + sample_struct_ptr = iox2_sample_t::alloc(); + deleter = iox2_sample_t::dealloc; + } + debug_assert!(!sample_struct_ptr.is_null()); + + (sample_struct_ptr, deleter) + }; let subscriber = &mut *subscriber_handle.as_type(); match subscriber.service_type { iox2_service_type_e::IPC => match subscriber.value.as_ref().ipc.receive() { Ok(Some(sample)) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); (*sample_struct_ptr).init( subscriber.service_type, SampleUnion::new_ipc(sample), @@ -226,6 +231,7 @@ pub unsafe extern "C" fn iox2_subscriber_receive( }, iox2_service_type_e::LOCAL => match subscriber.value.as_ref().local.receive() { Ok(Some(sample)) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); (*sample_struct_ptr).init( subscriber.service_type, SampleUnion::new_local(sample), From b3b2bfee0ddabf09d6b9da38a0a59c63f8f77982 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Sat, 27 Jul 2024 02:32:42 +0200 Subject: [PATCH 20/35] [#264] Fix more memory leaks in service_builder C API --- .../ffi/src/api/service_builder_event.rs | 196 +++++------------ .../ffi/src/api/service_builder_pub_sub.rs | 205 ++++++------------ 2 files changed, 122 insertions(+), 279 deletions(-) diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs index 0c6817fa5..120744779 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs @@ -18,7 +18,11 @@ use crate::api::{ PortFactoryEventUnion, ServiceBuilderUnion, IOX2_OK, }; -use iceoryx2::service::builder::event::{EventCreateError, EventOpenError, EventOpenOrCreateError}; +use iceoryx2::prelude::*; +use iceoryx2::service::builder::event::{ + Builder, EventCreateError, EventOpenError, EventOpenOrCreateError, +}; +use iceoryx2::service::port_factory::event::PortFactory; use core::ffi::c_int; use core::mem::ManuallyDrop; @@ -242,69 +246,13 @@ pub unsafe extern "C" fn iox2_service_builder_event_open_or_create( port_factory_struct_ptr: *mut iox2_port_factory_event_t, port_factory_handle_ptr: *mut iox2_port_factory_event_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_event_t) {} - let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); - deleter = iox2_port_factory_event_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - let service_type = service_builder_struct.service_type; - let service_builder = service_builder_struct - .value - .as_option_mut() - .take() - .unwrap_or_else(|| { - panic!("Trying to use an invalid 'iox2_service_builder_event_h'!"); - }); - (service_builder_struct.deleter)(service_builder_struct); - - match service_type { - iox2_service_type_e::IPC => { - let service_builder = ManuallyDrop::into_inner(service_builder.ipc); - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = ManuallyDrop::into_inner(service_builder.local); - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_event_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open_or_create(), + |service_builder| service_builder.open_or_create(), + ) } /// Opens an event service and returns a port factory to create notifiers and listeners. @@ -330,69 +278,13 @@ pub unsafe extern "C" fn iox2_service_builder_event_open( port_factory_struct_ptr: *mut iox2_port_factory_event_t, port_factory_handle_ptr: *mut iox2_port_factory_event_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_event_t) {} - let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); - deleter = iox2_port_factory_event_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - let service_type = service_builder_struct.service_type; - let service_builder = service_builder_struct - .value - .as_option_mut() - .take() - .unwrap_or_else(|| { - panic!("Trying to use an invalid 'iox2_service_builder_event_h'!"); - }); - (service_builder_struct.deleter)(service_builder_struct); - - match service_type { - iox2_service_type_e::IPC => { - let service_builder = ManuallyDrop::into_inner(service_builder.ipc); - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = ManuallyDrop::into_inner(service_builder.local); - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_event_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open(), + |service_builder| service_builder.open(), + ) } /// Creates an event service and returns a port factory to create notifiers and listeners. @@ -417,18 +309,40 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( service_builder_handle: iox2_service_builder_event_h, port_factory_struct_ptr: *mut iox2_port_factory_event_t, port_factory_handle_ptr: *mut iox2_port_factory_event_h, +) -> c_int { + iox2_service_builder_event_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.create(), + |service_builder| service_builder.create(), + ) +} + +unsafe fn iox2_service_builder_event_open_create_impl( + service_builder_handle: iox2_service_builder_event_h, + port_factory_struct_ptr: *mut iox2_port_factory_event_t, + port_factory_handle_ptr: *mut iox2_port_factory_event_h, + func_ipc: impl FnOnce(Builder) -> Result, E>, + func_local: impl FnOnce( + Builder, + ) -> Result, E>, ) -> c_int { debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_event_t) {} - let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); - deleter = iox2_port_factory_event_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); + let init_port_factory_struct_ptr = |port_factory_struct_ptr: *mut iox2_port_factory_event_t| { + let mut port_factory_struct_ptr = port_factory_struct_ptr; + fn no_op(_: *mut iox2_port_factory_event_t) {} + let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; + if port_factory_struct_ptr.is_null() { + port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); + deleter = iox2_port_factory_event_t::dealloc; + } + debug_assert!(!port_factory_struct_ptr.is_null()); + + (port_factory_struct_ptr, deleter) + }; let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; let service_type = service_builder_struct.service_type; @@ -446,13 +360,16 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); - match service_builder.create() { + match func_ipc(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryEventUnion::new_ipc(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -463,13 +380,16 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.event); - match service_builder.create() { + match func_local(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryEventUnion::new_local(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -478,8 +398,6 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( } } - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - IOX2_OK } diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs index cbd14f565..4ac1bae19 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs @@ -15,12 +15,16 @@ use crate::api::{ c_size_t, iox2_port_factory_pub_sub_h, iox2_port_factory_pub_sub_t, iox2_service_builder_pub_sub_h, iox2_service_builder_pub_sub_ref_h, iox2_service_type_e, - HandleToType, IntoCInt, PortFactoryPubSubUnion, ServiceBuilderUnion, IOX2_OK, + HandleToType, IntoCInt, NoUserHeaderFfi, PayloadFfi, PortFactoryPubSubUnion, + ServiceBuilderUnion, IOX2_OK, }; +use iceoryx2::prelude::*; use iceoryx2::service::builder::publish_subscribe::{ - PublishSubscribeCreateError, PublishSubscribeOpenError, PublishSubscribeOpenOrCreateError, + Builder, PublishSubscribeCreateError, PublishSubscribeOpenError, + PublishSubscribeOpenOrCreateError, }; +use iceoryx2::service::port_factory::publish_subscribe::PortFactory; use iceoryx2::service::static_config::message_type_details::{TypeDetail, TypeVariant}; use iceoryx2_bb_log::fatal_panic; @@ -375,69 +379,13 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open_or_create( port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} - let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); - deleter = iox2_port_factory_pub_sub_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - let service_type = service_builder_struct.service_type; - let service_builder = service_builder_struct - .value - .as_option_mut() - .take() - .unwrap_or_else(|| { - panic!("Trying to use an invalid 'iox2_service_builder_pub_sub_h'!"); - }); - (service_builder_struct.deleter)(service_builder_struct); - - match service_type { - iox2_service_type_e::IPC => { - let service_builder = ManuallyDrop::into_inner(service_builder.ipc); - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = ManuallyDrop::into_inner(service_builder.local); - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open_or_create(), + |service_builder| service_builder.open_or_create(), + ) } /// Opens a publish-subscribe service and returns a port factory to create publishers and subscribers. @@ -463,69 +411,13 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open( port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} - let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); - deleter = iox2_port_factory_pub_sub_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - let service_type = service_builder_struct.service_type; - let service_builder = service_builder_struct - .value - .as_option_mut() - .take() - .unwrap_or_else(|| { - panic!("Trying to use an invalid 'iox2_service_builder_pub_sub_h'!"); - }); - (service_builder_struct.deleter)(service_builder_struct); - - match service_type { - iox2_service_type_e::IPC => { - let service_builder = ManuallyDrop::into_inner(service_builder.ipc); - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = ManuallyDrop::into_inner(service_builder.local); - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open(), + |service_builder| service_builder.open(), + ) } /// Creates a publish-subscribe service and returns a port factory to create publishers and subscribers. @@ -550,18 +442,47 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( service_builder_handle: iox2_service_builder_pub_sub_h, port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, +) -> c_int { + iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.create(), + |service_builder| service_builder.create(), + ) +} + +unsafe fn iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle: iox2_service_builder_pub_sub_h, + port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, + port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, + func_ipc: impl FnOnce( + Builder, + ) + -> Result, E>, + func_local: impl FnOnce( + Builder, + ) -> Result< + PortFactory, + E, + >, ) -> c_int { debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} - let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); - deleter = iox2_port_factory_pub_sub_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); + let init_port_factory_struct_ptr = + |port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t| { + let mut port_factory_struct_ptr = port_factory_struct_ptr; + fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} + let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; + if port_factory_struct_ptr.is_null() { + port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); + deleter = iox2_port_factory_pub_sub_t::dealloc; + } + debug_assert!(!port_factory_struct_ptr.is_null()); + + (port_factory_struct_ptr, deleter) + }; let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; let service_type = service_builder_struct.service_type; @@ -579,13 +500,16 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - match service_builder.create() { + match func_ipc(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryPubSubUnion::new_ipc(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -596,13 +520,16 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - match service_builder.create() { + match func_local(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryPubSubUnion::new_local(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -611,8 +538,6 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( } } - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - IOX2_OK } From a9022f0e6499dee63dd374ff8712b8360e4ec995 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 13:40:53 +0200 Subject: [PATCH 21/35] [#264] Add c documentation --- examples/cxx/README.md | 23 ----- examples/cxx/publish_subscribe/README.md | 42 ++++++++ iceoryx2-ffi/ffi/src/api/publisher.rs | 121 +++++++++-------------- iceoryx2-ffi/ffi/src/api/sample.rs | 19 ++++ iceoryx2-ffi/ffi/src/api/sample_mut.rs | 42 +++++++- iceoryx2-ffi/ffi/src/api/subscriber.rs | 61 +----------- 6 files changed, 149 insertions(+), 159 deletions(-) create mode 100644 examples/cxx/publish_subscribe/README.md diff --git a/examples/cxx/README.md b/examples/cxx/README.md index 5467d0066..06290153b 100644 --- a/examples/cxx/README.md +++ b/examples/cxx/README.md @@ -8,26 +8,3 @@ In the repository root folder, execute this steps. cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON cmake --build target/ffi/build ``` - -## Run Examples - -### Publish-Subscribe - -Run in two separate terminals. Note, currently the examples run for 10 seconds. - - -```bash -target/ffi/build/examples/cxx/publish_subscribe/example_cxx_publisher -``` - - -```bash -target/ffi/build/examples/cxx/publish_subscribe/example_cxx_subscriber -``` - -### Discovery - - -```bash -target/ffi/build/examples/cxx/discovery/example_cxx_discovery -``` diff --git a/examples/cxx/publish_subscribe/README.md b/examples/cxx/publish_subscribe/README.md new file mode 100644 index 000000000..9de5dd4e1 --- /dev/null +++ b/examples/cxx/publish_subscribe/README.md @@ -0,0 +1,42 @@ +# Publish-Subscribe + +## Running The Example + +This example illustrates a robust publisher-subscriber communication +pattern between two separate processes. The publisher sends two +messages every second, each containing [`TransmissionData`]. On the +receiving end, the subscriber checks for new data every second. + +The subscriber is printing the sample on the console whenever new data arrives. + +First you have to build the C++ examples: + +```sh +cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON +cmake --build target/ffi/build +``` + +To observe this dynamic communication in action, open two separate terminals +and execute the following commands: + +**Terminal 1** + +```sh +./target/ffi/build/examples/cxx/publish_subscribe/example_cxx_publish_subscribe_subscriber +``` + +**Terminal 2** + +```sh +./target/ffi/build/examples/cxx/publish_subscribe/example_cxx_publish_subscribe_publisher +``` + +Feel free to run multiple instances of publisher or subscriber processes +simultaneously to explore how iceoryx2 handles publisher-subscriber communication +efficiently. + +You may hit the maximum supported number of ports when too many publisher or +subscriber processes run. Take a look at the [iceoryx2 config](../../../config) to set the +limits globally or at the +[API of the Service builder](https://docs.rs/iceoryx2/latest/iceoryx2/service/index.html) +to set them for a single service. diff --git a/iceoryx2-ffi/ffi/src/api/publisher.rs b/iceoryx2-ffi/ffi/src/api/publisher.rs index bf29737d8..cf342b7e7 100644 --- a/iceoryx2-ffi/ffi/src/api/publisher.rs +++ b/iceoryx2-ffi/ffi/src/api/publisher.rs @@ -20,6 +20,8 @@ use iceoryx2::prelude::*; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; +use super::{c_size_t, iox2_sample_mut_h, iox2_sample_mut_t, IntoCInt}; + use core::ffi::{c_int, c_void}; use core::mem::ManuallyDrop; @@ -200,6 +202,18 @@ unsafe fn send_copy( // BEGIN C API +/// This function casts an owning [`iox2_publisher_h`] into a non-owning [`iox2_publisher_ref_h`] +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create) +/// +/// Returns a [`iox2_publisher_ref_h`] +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * The `handle` is still valid after the call to this function. #[no_mangle] pub unsafe extern "C" fn iox2_cast_publisher_ref_h( handle: iox2_publisher_h, @@ -209,6 +223,23 @@ pub unsafe extern "C" fn iox2_cast_publisher_ref_h( (*handle.as_type()).as_ref_handle() as *mut _ as _ } +/// Sends a copy of the provided data via the publisher. The data must be copyable via `memcpy`. +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create) +/// * `data_ptr` pointer to the payload that shall be transmitted +/// * `data_len` the size of the payload in bytes +/// * `number_of_recipients` (optional) used to store the number of subscriber that received the data +/// +/// Return [`IOX2_OK`] on success, otherwise [`iox2_publisher_send_error_e`]. +/// +/// # Safety +/// +/// * `publisher_handle` is valid, non-null and was obtained via [`iox2_cast_publisher_ref_h`] +/// * `data_ptr` non-null pointer to a valid position in memory +/// * `data_len` the size of the payload memory +/// * `number_of_recipients` can be null, otherwise a valid pointer to an [`usize`] #[no_mangle] pub unsafe extern "C" fn iox2_publisher_send_copy( publisher_handle: iox2_publisher_ref_h, @@ -238,6 +269,23 @@ pub unsafe extern "C" fn iox2_publisher_send_copy( } } +/// Loans memory from the publishers data segment. +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create) +/// * `number_of_elements` defines the number of elements that shall be loaned. The elements were +/// defined via [`iox2_service_builder_pub_sub_set_payload_type_details()`](crate::iox2_service_builder_pub_sub_set_payload_type_details). +/// * `sample_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_sample_mut_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// * `sample_handle_ptr` - An uninitialized or dangling [`iox2_sample_mut_h`] handle which will be initialized by this function call if a sample is obtained, otherwise it will be set to NULL. +/// +/// Return [`IOX2_OK`] on success, otherwise [`iox2_publisher_loan_error_e`]. +/// +/// # Safety +/// +/// * `publisher_handle` is valid, non-null and was obtained via [`iox2_cast_publisher_ref_h`] +/// * The `sample_handle_ptr` is pointing to a valid [`iox2_sample_mut_h`]. #[no_mangle] pub unsafe extern "C" fn iox2_publisher_loan( publisher_handle: iox2_publisher_ref_h, @@ -328,76 +376,3 @@ pub unsafe extern "C" fn iox2_publisher_drop(publisher_handle: iox2_publisher_h) } // END C API - -use core::time::Duration; -use iceoryx2_bb_log::set_log_level; - -use super::{c_size_t, iox2_sample_mut_h, iox2_sample_mut_t, IntoCInt}; - -const CYCLE_TIME: Duration = Duration::from_secs(1); - -#[no_mangle] -pub extern "C" fn run_publisher(seconds: u32) -> i32 { - set_log_level(iceoryx2_bb_log::LogLevel::Info); - - let service_name = ServiceName::new("Hello/from/C"); - let node = NodeBuilder::new().create::(); - - if service_name.is_err() || node.is_err() { - return -1; - } - - let service_name = service_name.unwrap(); - let node = node.unwrap(); - - let service = node - .service_builder(&service_name) - .publish_subscribe::() - .open_or_create(); - - if service.is_err() { - return -1; - } - - let service = service.unwrap(); - - let publisher = service.publisher_builder().create(); - - if publisher.is_err() { - return -1; - } - - let publisher = publisher.unwrap(); - - let mut counter: u64 = 0; - - let mut remaining_seconds = seconds; - - while let NodeEvent::Tick = node.wait(CYCLE_TIME) { - counter += 1; - let sample = publisher.loan_uninit(); - - if sample.is_err() { - return -1; - } - - let sample = sample.unwrap(); - - let sample = sample.write_payload(counter); - - if sample.send().is_err() { - return -1; - } - - println!("Send sample {} ...", counter); - - remaining_seconds = remaining_seconds.saturating_sub(1); - if remaining_seconds == 0 { - break; - } - } - - println!("exit"); - - 0 -} diff --git a/iceoryx2-ffi/ffi/src/api/sample.rs b/iceoryx2-ffi/ffi/src/api/sample.rs index a5ddf364d..fa8b4b0f6 100644 --- a/iceoryx2-ffi/ffi/src/api/sample.rs +++ b/iceoryx2-ffi/ffi/src/api/sample.rs @@ -100,12 +100,31 @@ impl HandleToType for iox2_sample_ref_h { // BEGIN C API +/// This function casts an owning [`iox2_sample_h`] into a non-owning [`iox2_sample_ref_h`] +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive()) +/// +/// Returns a [`iox2_sample_ref_h`] +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * The `handle` is still valid after the call to this function. #[no_mangle] pub unsafe extern "C" fn iox2_cast_sample_ref_h(handle: iox2_sample_h) -> iox2_sample_ref_h { debug_assert!(!handle.is_null()); (*handle.as_type()).as_ref_handle() as *mut _ as _ } +/// Acquires the samples payload. +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive()) +/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. +/// * `payload_len` a valid, non-null pointer pointing to a [`c_size_t`]. #[no_mangle] pub unsafe extern "C" fn iox2_sample_payload( sample_handle: iox2_sample_ref_h, diff --git a/iceoryx2-ffi/ffi/src/api/sample_mut.rs b/iceoryx2-ffi/ffi/src/api/sample_mut.rs index d68199e68..80d53b113 100644 --- a/iceoryx2-ffi/ffi/src/api/sample_mut.rs +++ b/iceoryx2-ffi/ffi/src/api/sample_mut.rs @@ -104,6 +104,18 @@ impl HandleToType for iox2_sample_mut_ref_h { // BEGIN C API +/// This function casts an owning [`iox2_sample_mut_h`] into a non-owning [`iox2_sample_mut_ref_h`] +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// +/// Returns a [`iox2_sample_mut_ref_h`] +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * The `handle` is still valid after the call to this function. #[no_mangle] pub unsafe extern "C" fn iox2_cast_sample_mut_ref_h( handle: iox2_sample_mut_h, @@ -112,6 +124,13 @@ pub unsafe extern "C" fn iox2_cast_sample_mut_ref_h( (*handle.as_type()).as_ref_handle() as *mut _ as _ } +/// Acquires the samples mutable payload. +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. +/// * `payload_len` a valid, non-null pointer pointing to a [`c_size_t`]. #[no_mangle] pub unsafe extern "C" fn iox2_sample_mut_payload_mut( sample_handle: iox2_sample_mut_ref_h, @@ -133,6 +152,13 @@ pub unsafe extern "C" fn iox2_sample_mut_payload_mut( *payload_len = payload.len(); } +/// Acquires the samples payload. +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. +/// * `payload_len` a valid, non-null pointer pointing to a [`c_size_t`]. #[no_mangle] pub unsafe extern "C" fn iox2_sample_mut_payload( sample_handle: iox2_sample_mut_ref_h, @@ -154,13 +180,19 @@ pub unsafe extern "C" fn iox2_sample_mut_payload( *payload_len = payload.len(); } +/// Takes the ownership of the sample and sends it +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// * `number_of_recipients`, can be null or must point to a valid [`c_size_t`] to store the number +/// of subscribers that received the sample #[no_mangle] pub unsafe extern "C" fn iox2_sample_mut_send( sample_handle: iox2_sample_mut_h, number_of_recipients: *mut c_size_t, ) -> c_int { debug_assert!(!sample_handle.is_null()); - debug_assert!(!number_of_recipients.is_null()); let sample_struct = &mut *sample_handle.as_type(); let service_type = sample_struct.service_type; @@ -177,7 +209,9 @@ pub unsafe extern "C" fn iox2_sample_mut_send( let sample = ManuallyDrop::into_inner(sample.ipc); match sample.assume_init().send() { Ok(v) => { - *number_of_recipients = v; + if !number_of_recipients.is_null() { + *number_of_recipients = v; + } } Err(e) => { (sample_struct.deleter)(sample_struct); @@ -189,7 +223,9 @@ pub unsafe extern "C" fn iox2_sample_mut_send( let sample = ManuallyDrop::into_inner(sample.local); match sample.assume_init().send() { Ok(v) => { - *number_of_recipients = v; + if !number_of_recipients.is_null() { + *number_of_recipients = v; + } } Err(e) => { (sample_struct.deleter)(sample_struct); diff --git a/iceoryx2-ffi/ffi/src/api/subscriber.rs b/iceoryx2-ffi/ffi/src/api/subscriber.rs index a32b2cd5c..a0b67e6fa 100644 --- a/iceoryx2-ffi/ffi/src/api/subscriber.rs +++ b/iceoryx2-ffi/ffi/src/api/subscriber.rs @@ -189,6 +189,7 @@ pub unsafe extern "C" fn iox2_subscriber_buffer_size( /// # Safety /// /// * The `subscriber_handle` is still valid after the return of this function and can be use in another function call. +/// * The `sample_handle_ptr` is pointing to a valid [`iox2_sample_h`]. #[no_mangle] pub unsafe extern "C" fn iox2_subscriber_receive( subscriber_handle: iox2_subscriber_ref_h, @@ -276,63 +277,3 @@ pub unsafe extern "C" fn iox2_subscriber_drop(subscriber_handle: iox2_subscriber } // END C API - -use core::time::Duration; -use iceoryx2_bb_log::set_log_level; - -const CYCLE_TIME: Duration = Duration::from_secs(1); - -#[no_mangle] -pub extern "C" fn run_subscriber(seconds: u32) -> i32 { - set_log_level(iceoryx2_bb_log::LogLevel::Info); - - let service_name = ServiceName::new("Hello/from/C"); - let node = NodeBuilder::new().create::(); - - if service_name.is_err() || node.is_err() { - return -1; - } - - let service_name = service_name.unwrap(); - let node = node.unwrap(); - - let service = node - .service_builder(&service_name) - .publish_subscribe::() - .open_or_create(); - - if service.is_err() { - return -1; - } - - let service = service.unwrap(); - - let subscriber = service.subscriber_builder().create(); - - if subscriber.is_err() { - return -1; - } - - let subscriber = subscriber.unwrap(); - - let mut remaining_seconds = seconds; - - while let NodeEvent::Tick = node.wait(CYCLE_TIME) { - loop { - match subscriber.receive() { - Ok(Some(sample)) => println!("received: {:?}", *sample), - Ok(None) => break, - Err(_) => return -1, - } - } - - remaining_seconds = remaining_seconds.saturating_sub(1); - if remaining_seconds == 0 { - break; - } - } - - println!("exit"); - - 0 -} From 563fd98cea33cc07750c51c4121ece5943662b0d Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 15:42:05 +0200 Subject: [PATCH 22/35] [#264] Add c publisher example --- examples/c/publish_subscribe/src/publisher.c | 96 +++++++++++++++++-- examples/c/publish_subscribe/src/subscriber.c | 3 +- .../publish_subscribe/src/transmission_data.h | 24 +++++ iceoryx2-ffi/ffi/src/api/sample_mut.rs | 14 +-- 4 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 examples/c/publish_subscribe/src/transmission_data.h diff --git a/examples/c/publish_subscribe/src/publisher.c b/examples/c/publish_subscribe/src/publisher.c index 5ce78926b..2294f5265 100644 --- a/examples/c/publish_subscribe/src/publisher.c +++ b/examples/c/publish_subscribe/src/publisher.c @@ -11,23 +11,105 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/iceoryx2.h" +#include "transmission_data.h" +#include #include #include +#include int main(void) { + // create new node iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); iox2_node_h node_handle = NULL; - int ret_val = iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle); - if (ret_val != IOX2_OK) { - printf("Could not create node! Error code: %i", ret_val); - return -1; + if (iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle) != IOX2_OK) { + printf("Could not create node!\n"); + goto end; } - const uint32_t NUMBER_OF_SECONDS_TO_RUN = 10; - ret_val = run_publisher(NUMBER_OF_SECONDS_TO_RUN); + // create service name + const char* service_name_value = "My/Funk/ServiceName"; + iox2_service_name_h service_name = NULL; + if (iox2_service_name_new(NULL, service_name_value, strlen(service_name_value), &service_name) != IOX2_OK) { + printf("Unable to create service name!\n"); + goto drop_node; + } + + // create service + iox2_service_name_ptr service_name_ptr = iox2_cast_service_name_ptr(service_name); + iox2_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); + iox2_service_builder_pub_sub_h service_builder_pub_sub = iox2_service_builder_pub_sub(service_builder); + iox2_service_builder_pub_sub_ref_h service_builder_pub_sub_ref = + iox2_cast_service_builder_pub_sub_ref_h(service_builder_pub_sub); + + // set pub sub payload type + const char* payload_type_name = "16TransmissionData"; + if (iox2_service_builder_pub_sub_set_payload_type_details(service_builder_pub_sub_ref, + iox2_type_variant_e_FIXED_SIZE, + payload_type_name, + strlen(payload_type_name), + sizeof(struct TransmissionData), + alignof(struct TransmissionData)) + != IOX2_OK) { + printf("Unable to set type details\n"); + goto drop_node; + } + iox2_port_factory_pub_sub_h service = NULL; + if (iox2_service_builder_pub_sub_open_or_create(service_builder_pub_sub, NULL, &service) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + // create publisher + iox2_port_factory_pub_sub_ref_h ref_service = iox2_cast_port_factory_pub_sub_ref_h(service); + iox2_port_factory_publisher_builder_h publisher_builder = + iox2_port_factory_pub_sub_publisher_builder(ref_service, NULL); + iox2_publisher_h publisher = NULL; + if (iox2_port_factory_publisher_builder_create(publisher_builder, NULL, &publisher) != IOX2_OK) { + printf("Unable to create publisher!\n"); + goto drop_service; + } + iox2_publisher_ref_h publisher_ref = iox2_cast_publisher_ref_h(publisher); + + uint64_t counter = 0; + while (iox2_node_wait(node_ref_handle, 1, 0) == iox2_node_event_e_TICK) { + counter += 1; + + // loan sample + iox2_sample_mut_h sample = NULL; + if (iox2_publisher_loan(publisher_ref, 1, NULL, &sample) != IOX2_OK) { + printf("Failed to loan sample\n"); + goto drop_publisher; + } + iox2_sample_mut_ref_h sample_ref = iox2_cast_sample_mut_ref_h(sample); + + // write payload + struct TransmissionData* payload; + iox2_sample_mut_payload_mut(sample_ref, (void**) &payload, NULL); + payload->x = counter; + payload->y = counter * 3; + payload->funky = counter * 812.12; + + // send sample + if (iox2_sample_mut_send(sample, NULL) != IOX2_OK) { + printf("Failed to send sample\n"); + goto drop_publisher; + } + + printf("Send sample %lu ...\n", counter); + } + + +drop_publisher: + iox2_publisher_drop(publisher); + +drop_service: + iox2_port_factory_pub_sub_drop(service); +drop_node: iox2_node_drop(node_handle); - return ret_val; +end: + return 0; } diff --git a/examples/c/publish_subscribe/src/subscriber.c b/examples/c/publish_subscribe/src/subscriber.c index 63cedf890..47adbcbb1 100644 --- a/examples/c/publish_subscribe/src/subscriber.c +++ b/examples/c/publish_subscribe/src/subscriber.c @@ -15,6 +15,5 @@ #include int main(void) { - const uint32_t NUMBER_OF_SECONDS_TO_RUN = 10; - return run_subscriber(NUMBER_OF_SECONDS_TO_RUN); + return 0; } diff --git a/examples/c/publish_subscribe/src/transmission_data.h b/examples/c/publish_subscribe/src/transmission_data.h new file mode 100644 index 000000000..b9130321a --- /dev/null +++ b/examples/c/publish_subscribe/src/transmission_data.h @@ -0,0 +1,24 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#ifndef IOX2_EXAMPLES_TRANSMISSION_DATA_H +#define IOX2_EXAMPLES_TRANSMISSION_DATA_H + +#include + +struct TransmissionData { + int32_t x; + int32_t y; + double funky; +}; + +#endif diff --git a/iceoryx2-ffi/ffi/src/api/sample_mut.rs b/iceoryx2-ffi/ffi/src/api/sample_mut.rs index 80d53b113..4d6fd34dd 100644 --- a/iceoryx2-ffi/ffi/src/api/sample_mut.rs +++ b/iceoryx2-ffi/ffi/src/api/sample_mut.rs @@ -130,7 +130,7 @@ pub unsafe extern "C" fn iox2_cast_sample_mut_ref_h( /// /// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) /// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. -/// * `payload_len` a valid, non-null pointer pointing to a [`c_size_t`]. +/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`]. #[no_mangle] pub unsafe extern "C" fn iox2_sample_mut_payload_mut( sample_handle: iox2_sample_mut_ref_h, @@ -139,7 +139,6 @@ pub unsafe extern "C" fn iox2_sample_mut_payload_mut( ) { debug_assert!(!sample_handle.is_null()); debug_assert!(!payload_ptr.is_null()); - debug_assert!(!payload_len.is_null()); let sample = &mut *sample_handle.as_type(); @@ -149,7 +148,9 @@ pub unsafe extern "C" fn iox2_sample_mut_payload_mut( }; *payload_ptr = payload.as_mut_ptr().cast(); - *payload_len = payload.len(); + if !payload_len.is_null() { + *payload_len = payload.len(); + } } /// Acquires the samples payload. @@ -158,7 +159,7 @@ pub unsafe extern "C" fn iox2_sample_mut_payload_mut( /// /// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) /// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. -/// * `payload_len` a valid, non-null pointer pointing to a [`c_size_t`]. +/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`]. #[no_mangle] pub unsafe extern "C" fn iox2_sample_mut_payload( sample_handle: iox2_sample_mut_ref_h, @@ -167,7 +168,6 @@ pub unsafe extern "C" fn iox2_sample_mut_payload( ) { debug_assert!(!sample_handle.is_null()); debug_assert!(!payload_ptr.is_null()); - debug_assert!(!payload_len.is_null()); let sample = &mut *sample_handle.as_type(); @@ -177,7 +177,9 @@ pub unsafe extern "C" fn iox2_sample_mut_payload( }; *payload_ptr = payload.as_ptr().cast(); - *payload_len = payload.len(); + if !payload_len.is_null() { + *payload_len = payload.len(); + } } /// Takes the ownership of the sample and sends it From 6fdaa532d7fcfc6d732d0e48f9c7a0fe500e8d81 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 15:53:31 +0200 Subject: [PATCH 23/35] [#264] Add c subscriber example --- examples/README.md | 2 +- examples/c/publish_subscribe/README.md | 42 +++++++++ examples/c/publish_subscribe/src/subscriber.c | 90 +++++++++++++++++++ iceoryx2-ffi/ffi/src/api/sample.rs | 7 +- 4 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 examples/c/publish_subscribe/README.md diff --git a/examples/README.md b/examples/README.md index 41913404c..94cba053e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -49,7 +49,7 @@ they interact and exchange data. | discovery | [Rust](rust/discovery) | List all available services in a system. | | docker | [all](rust/docker) | Communicate between different docker containers and the host. | | event | [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them.| -| publish subscribe | [C++](rust/publish_subscribe) [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | +| publish subscribe | [C](c/publish_subscribe) [C++](cxx/publish_subscribe) [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | | publish subscribe dynamic data | [Rust](rust/publish_subscribe_dynamic_data) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) and payload data that has a dynamic size. | | publish subscribe with user header | [Rust](rust/publish_subscribe_with_user_header) | Add a user header to the payload (samples) to transfer additional information. | | service attributes | [Rust](rust/service_attributes) | Creates a service with custom attributes that are available to every endpoint. If the attributes are not compatible the service will not open. | diff --git a/examples/c/publish_subscribe/README.md b/examples/c/publish_subscribe/README.md new file mode 100644 index 000000000..07a649514 --- /dev/null +++ b/examples/c/publish_subscribe/README.md @@ -0,0 +1,42 @@ +# Publish-Subscribe + +## Running The Example + +This example illustrates a robust publisher-subscriber communication +pattern between two separate processes. The publisher sends two +messages every second, each containing [`TransmissionData`]. On the +receiving end, the subscriber checks for new data every second. + +The subscriber is printing the sample on the console whenever new data arrives. + +First you have to build the C++ examples: + +```sh +cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON +cmake --build target/ffi/build +``` + +To observe this dynamic communication in action, open two separate terminals +and execute the following commands: + +**Terminal 1** + +```sh +./target/ffi/build/examples/c/publish_subscribe/example_c_publish_subscribe_subscriber +``` + +**Terminal 2** + +```sh +./target/ffi/build/examples/c/publish_subscribe/example_c_publish_subscribe_publisher +``` + +Feel free to run multiple instances of publisher or subscriber processes +simultaneously to explore how iceoryx2 handles publisher-subscriber communication +efficiently. + +You may hit the maximum supported number of ports when too many publisher or +subscriber processes run. Take a look at the [iceoryx2 config](../../../config) to set the +limits globally or at the +[API of the Service builder](https://docs.rs/iceoryx2/latest/iceoryx2/service/index.html) +to set them for a single service. diff --git a/examples/c/publish_subscribe/src/subscriber.c b/examples/c/publish_subscribe/src/subscriber.c index 47adbcbb1..e98c99597 100644 --- a/examples/c/publish_subscribe/src/subscriber.c +++ b/examples/c/publish_subscribe/src/subscriber.c @@ -11,9 +11,99 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/iceoryx2.h" +#include "transmission_data.h" +#include #include +#include +#include int main(void) { + // create new node + iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); + iox2_node_h node_handle = NULL; + if (iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle) != IOX2_OK) { + printf("Could not create node!\n"); + goto end; + } + + // create service name + const char* service_name_value = "My/Funk/ServiceName"; + iox2_service_name_h service_name = NULL; + if (iox2_service_name_new(NULL, service_name_value, strlen(service_name_value), &service_name) != IOX2_OK) { + printf("Unable to create service name!\n"); + goto drop_node; + } + + // create service + iox2_service_name_ptr service_name_ptr = iox2_cast_service_name_ptr(service_name); + iox2_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); + iox2_service_builder_pub_sub_h service_builder_pub_sub = iox2_service_builder_pub_sub(service_builder); + iox2_service_builder_pub_sub_ref_h service_builder_pub_sub_ref = + iox2_cast_service_builder_pub_sub_ref_h(service_builder_pub_sub); + + // set pub sub payload type + const char* payload_type_name = "16TransmissionData"; + if (iox2_service_builder_pub_sub_set_payload_type_details(service_builder_pub_sub_ref, + iox2_type_variant_e_FIXED_SIZE, + payload_type_name, + strlen(payload_type_name), + sizeof(struct TransmissionData), + alignof(struct TransmissionData)) + != IOX2_OK) { + printf("Unable to set type details\n"); + goto drop_node; + } + iox2_port_factory_pub_sub_h service = NULL; + if (iox2_service_builder_pub_sub_open_or_create(service_builder_pub_sub, NULL, &service) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + // create subscriber + iox2_port_factory_pub_sub_ref_h ref_service = iox2_cast_port_factory_pub_sub_ref_h(service); + iox2_port_factory_subscriber_builder_h subscriber_builder = + iox2_port_factory_pub_sub_subscriber_builder(ref_service, NULL); + iox2_subscriber_h subscriber = NULL; + if (iox2_port_factory_subscriber_builder_create(subscriber_builder, NULL, &subscriber) != IOX2_OK) { + printf("Unable to create subscriber!\n"); + goto drop_service; + } + iox2_subscriber_ref_h subscriber_ref = iox2_cast_subscriber_ref_h(subscriber); + + uint64_t counter = 0; + while (iox2_node_wait(node_ref_handle, 1, 0) == iox2_node_event_e_TICK) { + counter += 1; + + // receive sample + iox2_sample_h sample = NULL; + if (iox2_subscriber_receive(subscriber_ref, NULL, &sample) != IOX2_OK) { + printf("Failed to receive sample\n"); + goto drop_subscriber; + } + + if (sample != NULL) { + iox2_sample_ref_h sample_ref = iox2_cast_sample_ref_h(sample); + struct TransmissionData* payload = NULL; + iox2_sample_payload(sample_ref, (const void**) &payload, NULL); + + printf( + "received: TransmissionData { .x: %d, .y: %d, .funky: %lf}\n", payload->x, payload->y, payload->funky); + iox2_sample_drop(sample); + } + } + + +drop_subscriber: + iox2_subscriber_drop(subscriber); + +drop_service: + iox2_port_factory_pub_sub_drop(service); + +drop_node: + iox2_node_drop(node_handle); + +end: return 0; } diff --git a/iceoryx2-ffi/ffi/src/api/sample.rs b/iceoryx2-ffi/ffi/src/api/sample.rs index fa8b4b0f6..cbbf1c4f9 100644 --- a/iceoryx2-ffi/ffi/src/api/sample.rs +++ b/iceoryx2-ffi/ffi/src/api/sample.rs @@ -124,7 +124,7 @@ pub unsafe extern "C" fn iox2_cast_sample_ref_h(handle: iox2_sample_h) -> iox2_s /// /// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive()) /// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. -/// * `payload_len` a valid, non-null pointer pointing to a [`c_size_t`]. +/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`]. #[no_mangle] pub unsafe extern "C" fn iox2_sample_payload( sample_handle: iox2_sample_ref_h, @@ -133,7 +133,6 @@ pub unsafe extern "C" fn iox2_sample_payload( ) { debug_assert!(!sample_handle.is_null()); debug_assert!(!payload_ptr.is_null()); - debug_assert!(!payload_len.is_null()); let sample = &mut *sample_handle.as_type(); @@ -143,7 +142,9 @@ pub unsafe extern "C" fn iox2_sample_payload( }; *payload_ptr = payload.as_ptr().cast(); - *payload_len = payload.len(); + if !payload_len.is_null() { + *payload_len = payload.len(); + } } /// This function needs to be called to destroy the sample! From 2f1cc05de1e7b18faab74eb5134de8afea5049bd Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 16:45:46 +0200 Subject: [PATCH 24/35] [#264] Add pub sub tests --- iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 4 +- iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp | 2 +- .../src/service_publish_subscribe_tests.cpp | 64 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 13b112e90..62223384e 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -135,6 +135,8 @@ inline auto Publisher::id() const -> UniquePublisherId { template inline auto Publisher::send_copy(const Payload& payload) const -> iox::expected { + static_assert(std::is_trivially_copyable::value, ""); + auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); size_t number_of_recipients = 0; @@ -166,7 +168,7 @@ inline auto Publisher::loan_uninit() template inline auto Publisher::loan() -> iox::expected, PublisherLoanError> { - IOX_TODO(); + return loan_uninit().and_then([](auto& sample) { new (&sample.payload_mut()) Payload(); }); } template diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index a2401e5d9..088f8c927 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -206,7 +206,7 @@ inline auto SampleMut::payload_mut() -> Payload& { template template inline void SampleMut::write_payload(T&& value) { - new (&payload_mut()) Payload(std::forward(value)); + new (&payload_mut()) Payload(std::forward(value)); } template diff --git a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp index 3abd36779..0c93239c2 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp @@ -159,4 +159,68 @@ TYPED_TEST(ServicePublishSubscribeTest, open_or_create_existing_service_with_wro ASSERT_TRUE(sut.has_error()); ASSERT_THAT(sut.error(), Eq(PublishSubscribeOpenOrCreateError::OpenIncompatibleTypes)); } + +TYPED_TEST(ServicePublishSubscribeTest, send_copy_receive_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + auto sut_publisher = service.publisher_builder().create().expect(""); + auto sut_subscriber = service.subscriber_builder().create().expect(""); + + const uint64_t payload = 123; + sut_publisher.send_copy(payload).expect(""); + auto sample = sut_subscriber.receive().expect(""); + + ASSERT_TRUE(sample.has_value()); + ASSERT_THAT(**sample, Eq(payload)); +} + +TYPED_TEST(ServicePublishSubscribeTest, loan_uninit_send_receive_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + auto sut_publisher = service.publisher_builder().create().expect(""); + auto sut_subscriber = service.subscriber_builder().create().expect(""); + + auto sample = sut_publisher.loan_uninit().expect(""); + const uint64_t payload = 78123791; + sample.write_payload(payload); + send_sample(std::move(sample)).expect(""); + auto recv_sample = sut_subscriber.receive().expect(""); + + ASSERT_TRUE(recv_sample.has_value()); + ASSERT_THAT(**recv_sample, Eq(payload)); +} + +TYPED_TEST(ServicePublishSubscribeTest, loan_send_receive_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + auto sut_publisher = service.publisher_builder().create().expect(""); + auto sut_subscriber = service.subscriber_builder().create().expect(""); + + auto sample = sut_publisher.loan().expect(""); + const uint64_t payload = 781891729871; + *sample = payload; + send_sample(std::move(sample)).expect(""); + auto recv_sample = sut_subscriber.receive().expect(""); + + ASSERT_TRUE(recv_sample.has_value()); + ASSERT_THAT(**recv_sample, Eq(payload)); +} } // namespace From 36613a8a1d1eb44bf7de515e8b8a037f34a50b29 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 19:38:31 +0200 Subject: [PATCH 25/35] [#264] Add StaticConfigPublishSubscribe --- iceoryx2-ffi/cxx/CMakeLists.txt | 2 + .../cxx/include/iox2/enum_translation.hpp | 16 +++++ .../cxx/include/iox2/message_type_details.hpp | 56 +++++++++------- .../iox2/port_factory_publish_subscribe.hpp | 11 ++-- .../iox2/static_config_publish_subscribe.hpp | 65 +++++++++++-------- .../cxx/include/iox2/type_variant.hpp | 30 +++++++++ iceoryx2-ffi/cxx/src/message_type_details.cpp | 52 +++++++++++++++ .../src/static_config_publish_subscribe.cpp | 53 +++++++++++++++ .../src/service_publish_subscribe_tests.cpp | 23 +++++++ .../ffi/src/api/message_type_details.rs | 61 +++++++++++++++++ iceoryx2-ffi/ffi/src/api/mod.rs | 4 ++ .../ffi/src/api/port_factory_pub_sub.rs | 27 ++++++++ .../ffi/src/api/service_builder_pub_sub.rs | 9 +++ .../api/static_config_publish_subscribe.rs | 45 +++++++++++++ 14 files changed, 401 insertions(+), 53 deletions(-) create mode 100644 iceoryx2-ffi/cxx/include/iox2/type_variant.hpp create mode 100644 iceoryx2-ffi/cxx/src/message_type_details.cpp create mode 100644 iceoryx2-ffi/cxx/src/static_config_publish_subscribe.cpp create mode 100644 iceoryx2-ffi/ffi/src/api/message_type_details.rs create mode 100644 iceoryx2-ffi/ffi/src/api/static_config_publish_subscribe.rs diff --git a/iceoryx2-ffi/cxx/CMakeLists.txt b/iceoryx2-ffi/cxx/CMakeLists.txt index 802a04e18..7a8a7046d 100644 --- a/iceoryx2-ffi/cxx/CMakeLists.txt +++ b/iceoryx2-ffi/cxx/CMakeLists.txt @@ -39,6 +39,7 @@ add_library(iceoryx2-cxx-object-lib OBJECT src/config.cpp src/event_id.cpp src/listener.cpp + src/message_type_details.cpp src/node.cpp src/node_details.cpp src/node_name.cpp @@ -51,6 +52,7 @@ add_library(iceoryx2-cxx-object-lib OBJECT src/service_builder_event.cpp src/service_name.cpp src/static_config_event.cpp + src/static_config_publish_subscribe.cpp ) set_target_properties(iceoryx2-cxx-object-lib diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index 3f50dde4b..55bb71ef2 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -29,6 +29,7 @@ #include "iox2/service_error_enums.hpp" #include "iox2/service_type.hpp" #include "iox2/subscriber_error.hpp" +#include "iox2/type_variant.hpp" namespace iox { template <> @@ -541,6 +542,21 @@ constexpr auto from(const int value) noexcept -> IOX_UNREACHABLE(); } + +template <> +constexpr auto from(const int value) noexcept -> iox2::TypeVariant { + const auto v = static_cast(value); + switch (v) { + case iox2_type_variant_e_DYNAMIC: + return iox2::TypeVariant::Dynamic; + case iox2_type_variant_e_FIXED_SIZE: + return iox2::TypeVariant::FixedSize; + } + + IOX_UNREACHABLE(); +} + + } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp b/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp index 80dff636a..41d5db6d8 100644 --- a/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp @@ -13,44 +13,54 @@ #ifndef IOX2_MESSAGE_TYPE_DETAILS_HPP #define IOX2_MESSAGE_TYPE_DETAILS_HPP -#include "iox/string.hpp" -#include "iox2/iceoryx2_settings.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/type_variant.hpp" -#include namespace iox2 { -/// Defines if the type is a slice with a runtime-size -/// ([`TypeVariant::Dynamic`]) or if its a type that satisfies [`Sized`] -/// ([`TypeVariant::FixedSize`]). -enum class TypeVariant : uint8_t { - /// A fixed size type like [`u64`] - FixedSize, - /// A dynamic sized type like a slice - Dynamic, -}; - /// Contains all type details required to connect to a /// [`crate::service::Service`] -struct TypeDetail { +class TypeDetail { + public: /// The [`TypeVariant`] of the type - TypeVariant variant; - /// Contains the output of [`core::any::type_name()`]. - iox::string type_name; + auto variant() const -> TypeVariant; + + /// Contains the output of [`typeid().name`]. + auto type_name() const -> const char*; + /// The size of the underlying type. - uint64_t size; + auto size() const -> size_t; + /// The alignment of the underlying type. - uint64_t alignment; + auto alignment() const -> size_t; + + private: + friend class MessageTypeDetails; + explicit TypeDetail(iox2_type_detail_t value); + + iox2_type_detail_t m_value; }; -struct MessageTypeDetails { +/// Contains all type information to the header and payload type. +class MessageTypeDetails { + public: /// The [`TypeDetail`] of the header of a message, the first iceoryx2 /// internal part. - TypeDetail header; + auto header() const -> TypeDetail; + /// The [`TypeDetail`] of the user_header or the custom header, is located /// directly after the header. - TypeDetail user_header; + auto user_header() const -> TypeDetail; + /// The [`TypeDetail`] of the payload of the message, the last part. - TypeDetail payload; + auto payload() const -> TypeDetail; + + private: + friend class StaticConfigPublishSubscribe; + + explicit MessageTypeDetails(iox2_message_type_details_t value); + + iox2_message_type_details_t m_value; }; } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp index 84fc89123..53384c679 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp @@ -54,7 +54,7 @@ class PortFactoryPublishSubscribe { /// Returns the StaticConfig of the [`Service`]. /// Contains all settings that never change during the lifetime of the service. - auto static_config() const -> const StaticConfigPublishSubscribe&; + auto static_config() const -> StaticConfigPublishSubscribe; /// Returns the DynamicConfig of the [`Service`]. /// Contains all dynamic settings, like the current participants etc.. @@ -136,9 +136,12 @@ inline auto PortFactoryPublishSubscribe::attributes() co } template -inline auto -PortFactoryPublishSubscribe::static_config() const -> const StaticConfigPublishSubscribe& { - IOX_TODO(); +inline auto PortFactoryPublishSubscribe::static_config() const -> StaticConfigPublishSubscribe { + auto* ref_handle = iox2_cast_port_factory_pub_sub_ref_h(m_handle); + iox2_static_config_publish_subscribe_t static_config {}; + iox2_port_factory_pub_sub_static_config(ref_handle, &static_config); + + return StaticConfigPublishSubscribe(static_config); } template diff --git a/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp index 078e4461d..888336f14 100644 --- a/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp @@ -13,38 +13,51 @@ #ifndef IOX2_STATIC_CONFIG_PUBLISH_SUBSCRIBE_HPP #define IOX2_STATIC_CONFIG_PUBLISH_SUBSCRIBE_HPP -#include "iox/assertions_addendum.hpp" -#include "message_type_details.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/message_type_details.hpp" #include namespace iox2 { +/// The static configuration of an [`MessagingPattern::PublishSubscribe`] +/// based service. Contains all parameters that do not change during the lifetime of a +/// [`Service`]. class StaticConfigPublishSubscribe { public: - auto max_nodes() const -> uint64_t { - IOX_TODO(); - } - auto max_publishers() const -> uint64_t { - IOX_TODO(); - } - auto max_subscribers() const -> uint64_t { - IOX_TODO(); - } - auto history_size() const -> uint64_t { - IOX_TODO(); - } - auto subscriber_max_buffer_size() const -> uint64_t { - IOX_TODO(); - } - auto subscriber_max_borrowed_samples() const -> uint64_t { - IOX_TODO(); - } - auto has_safe_overflow() const -> bool { - IOX_TODO(); - } - auto message_type_details() const -> const MessageTypeDetails& { - IOX_TODO(); - } + /// Returns the maximum supported amount of [`Node`]s that can open the + /// [`Service`] in parallel. + auto max_nodes() const -> uint64_t; + + /// Returns the maximum supported amount of [`Publisher`] ports + auto max_publishers() const -> uint64_t; + + /// Returns the maximum supported amount of [`Subscriber`] ports + auto max_subscribers() const -> uint64_t; + + /// Returns the maximum history size that can be requested on connect. + auto history_size() const -> uint64_t; + + /// Returns the maximum supported buffer size for [`Subscriber`] port + auto subscriber_max_buffer_size() const -> uint64_t; + + /// Returns how many [`Sample`]s a [`Subscriber`] port can borrow in parallel at most. + auto subscriber_max_borrowed_samples() const -> uint64_t; + + /// Returns true if the [`Service`] safely overflows, otherwise false. Safe + /// overflow means that the [`Publisher`] will recycle the oldest + /// [`Sample`] from the [`Subscriber`] when its buffer is full. + auto has_safe_overflow() const -> bool; + + /// Returns the type details of the [`Service`]. + auto message_type_details() const -> MessageTypeDetails; + + private: + template + friend class PortFactoryPublishSubscribe; + + explicit StaticConfigPublishSubscribe(iox2_static_config_publish_subscribe_t value); + + iox2_static_config_publish_subscribe_t m_value; }; } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/type_variant.hpp b/iceoryx2-ffi/cxx/include/iox2/type_variant.hpp new file mode 100644 index 000000000..331a2ea5e --- /dev/null +++ b/iceoryx2-ffi/cxx/include/iox2/type_variant.hpp @@ -0,0 +1,30 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#ifndef IOX2_TYPE_VARIANT_HPP +#define IOX2_TYPE_VARIANT_HPP + +#include + +namespace iox2 { +/// Defines if the type is a slice with a runtime-size +/// ([`TypeVariant::Dynamic`]) or if its a type that satisfies [`Sized`] +/// ([`TypeVariant::FixedSize`]). +enum class TypeVariant : uint8_t { + /// A fixed size type like [`u64`] + FixedSize, + /// A dynamic sized type like a slice + Dynamic, +}; +} // namespace iox2 + +#endif diff --git a/iceoryx2-ffi/cxx/src/message_type_details.cpp b/iceoryx2-ffi/cxx/src/message_type_details.cpp new file mode 100644 index 000000000..9e41b60ef --- /dev/null +++ b/iceoryx2-ffi/cxx/src/message_type_details.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/message_type_details.hpp" +#include "iox/into.hpp" + +namespace iox2 { +TypeDetail::TypeDetail(iox2_type_detail_t value) + : m_value { value } { +} + +auto TypeDetail::variant() const -> TypeVariant { + return iox::into(static_cast(m_value.variant)); +} + +auto TypeDetail::type_name() const -> const char* { + return m_value.type_name; +} + +auto TypeDetail::size() const -> size_t { + return m_value.size; +} + +auto TypeDetail::alignment() const -> size_t { + return m_value.alignment; +} + +MessageTypeDetails::MessageTypeDetails(iox2_message_type_details_t value) + : m_value { value } { +} + +auto MessageTypeDetails::header() const -> TypeDetail { + return TypeDetail(m_value.header); +} + +auto MessageTypeDetails::user_header() const -> TypeDetail { + return TypeDetail(m_value.user_header); +} + +auto MessageTypeDetails::payload() const -> TypeDetail { + return TypeDetail(m_value.payload); +} +} // namespace iox2 diff --git a/iceoryx2-ffi/cxx/src/static_config_publish_subscribe.cpp b/iceoryx2-ffi/cxx/src/static_config_publish_subscribe.cpp new file mode 100644 index 000000000..a4dd9ff0e --- /dev/null +++ b/iceoryx2-ffi/cxx/src/static_config_publish_subscribe.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/static_config_publish_subscribe.hpp" + +namespace iox2 { +StaticConfigPublishSubscribe::StaticConfigPublishSubscribe(iox2_static_config_publish_subscribe_t value) + : m_value { value } { +} + +auto StaticConfigPublishSubscribe::max_nodes() const -> uint64_t { + return m_value.max_nodes; +} + +auto StaticConfigPublishSubscribe::max_publishers() const -> uint64_t { + return m_value.max_publishers; +} + +auto StaticConfigPublishSubscribe::max_subscribers() const -> uint64_t { + return m_value.max_subscribers; +} + +auto StaticConfigPublishSubscribe::history_size() const -> uint64_t { + return m_value.history_size; +} + +auto StaticConfigPublishSubscribe::subscriber_max_buffer_size() const -> uint64_t { + return m_value.subscriber_max_buffer_size; +} + +auto StaticConfigPublishSubscribe::subscriber_max_borrowed_samples() const -> uint64_t { + return m_value.subscriber_max_borrowed_samples; +} + +auto StaticConfigPublishSubscribe::has_safe_overflow() const -> bool { + return m_value.enable_safe_overflow; +} + +auto StaticConfigPublishSubscribe::message_type_details() const -> MessageTypeDetails { + return MessageTypeDetails(m_value.message_type_details); +} + + +} // namespace iox2 diff --git a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp index 0c93239c2..9ebd0991e 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp @@ -223,4 +223,27 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_send_receive_works) { ASSERT_TRUE(recv_sample.has_value()); ASSERT_THAT(**recv_sample, Eq(payload)); } + +TYPED_TEST(ServicePublishSubscribeTest, setting_service_properties_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name) + .template publish_subscribe() + .max_publishers(11) + .max_subscribers(12) + .create() + .expect(""); + + auto static_config = service.static_config(); + + ASSERT_THAT(static_config.max_publishers(), Eq(11)); + ASSERT_THAT(static_config.max_subscribers(), Eq(12)); + ASSERT_THAT(static_config.message_type_details().payload().size(), Eq(sizeof(uint64_t))); + ASSERT_THAT(static_config.message_type_details().payload().alignment(), Eq(alignof(uint64_t))); + ASSERT_THAT(static_config.message_type_details().payload().type_name(), StrEq(typeid(uint64_t).name())); +} } // namespace diff --git a/iceoryx2-ffi/ffi/src/api/message_type_details.rs b/iceoryx2-ffi/ffi/src/api/message_type_details.rs new file mode 100644 index 000000000..37f14d8fc --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/message_type_details.rs @@ -0,0 +1,61 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use core::ffi::c_char; + +use iceoryx2::service::static_config::message_type_details::*; + +use crate::iox2_type_variant_e; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct iox2_type_detail_t { + pub variant: iox2_type_variant_e, + pub type_name: [c_char; 256], + pub size: usize, + pub alignment: usize, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct iox2_message_type_details_t { + pub header: iox2_type_detail_t, + pub user_header: iox2_type_detail_t, + pub payload: iox2_type_detail_t, +} + +impl From<&TypeDetail> for iox2_type_detail_t { + fn from(value: &TypeDetail) -> Self { + Self { + variant: (&value.variant).into(), + type_name: core::array::from_fn(|n| { + if n < value.type_name.as_bytes().len() { + value.type_name.as_bytes()[n] as _ + } else { + 0 + } + }), + size: value.size, + alignment: value.alignment, + } + } +} + +impl From<&MessageTypeDetails> for iox2_message_type_details_t { + fn from(m: &MessageTypeDetails) -> Self { + Self { + header: (&m.header).into(), + user_header: (&m.user_header).into(), + payload: (&m.payload).into(), + } + } +} diff --git a/iceoryx2-ffi/ffi/src/api/mod.rs b/iceoryx2-ffi/ffi/src/api/mod.rs index 643cabc98..27efa9992 100644 --- a/iceoryx2-ffi/ffi/src/api/mod.rs +++ b/iceoryx2-ffi/ffi/src/api/mod.rs @@ -21,6 +21,7 @@ use core::ffi::{c_int, c_void}; mod config; mod event_id; mod listener; +mod message_type_details; mod node; mod node_builder; mod node_name; @@ -41,11 +42,13 @@ mod service_builder_event; mod service_builder_pub_sub; mod service_name; mod static_config_event; +mod static_config_publish_subscribe; mod subscriber; pub use config::*; pub use event_id::*; pub use listener::*; +pub use message_type_details::*; pub use node::*; pub use node_builder::*; pub use node_name::*; @@ -66,6 +69,7 @@ pub use service_builder_event::*; pub use service_builder_pub_sub::*; pub use service_name::*; pub use static_config_event::*; +pub use static_config_publish_subscribe::*; pub use subscriber::*; /// This constant signals an successful function call diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs index dabf5c247..99139dc83 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs @@ -18,6 +18,7 @@ use crate::api::{ iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi, PortFactoryPublisherBuilderUnion, PortFactorySubscriberBuilderUnion, }; +use crate::iox2_static_config_publish_subscribe_t; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::publish_subscribe::PortFactory; @@ -235,6 +236,32 @@ pub unsafe extern "C" fn iox2_port_factory_pub_sub_subscriber_builder( (*subscriber_builder_struct_ptr).as_handle() } +/// Set the values int the provided [`iox2_static_config_publish_subscribe_t`] pointer. +/// +/// # Safety +/// +/// * The `_handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +/// * The `static_config` must be a valid pointer and non-null. +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_static_config( + port_factory_handle: iox2_port_factory_pub_sub_ref_h, + static_config: *mut iox2_static_config_publish_subscribe_t, +) { + debug_assert!(!port_factory_handle.is_null()); + debug_assert!(!static_config.is_null()); + + let port_factory = &mut *port_factory_handle.as_type(); + + use iceoryx2::prelude::PortFactory; + let config = match port_factory.service_type { + iox2_service_type_e::IPC => port_factory.value.as_ref().ipc.static_config(), + iox2_service_type_e::LOCAL => port_factory.value.as_ref().local.static_config(), + }; + + *static_config = config.into(); +} + /// This function needs to be called to destroy the port factory! /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs index 4ac1bae19..4a39fc37b 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs @@ -167,6 +167,15 @@ pub enum iox2_type_variant_e { DYNAMIC, } +impl From<&TypeVariant> for iox2_type_variant_e { + fn from(value: &TypeVariant) -> Self { + match value { + TypeVariant::Dynamic => iox2_type_variant_e::DYNAMIC, + TypeVariant::FixedSize => iox2_type_variant_e::FIXED_SIZE, + } + } +} + #[repr(C)] #[derive(Copy, Clone)] pub enum iox2_type_detail_error_e { diff --git a/iceoryx2-ffi/ffi/src/api/static_config_publish_subscribe.rs b/iceoryx2-ffi/ffi/src/api/static_config_publish_subscribe.rs new file mode 100644 index 000000000..72fb09b2a --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/static_config_publish_subscribe.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(non_camel_case_types)] + +use iceoryx2::service::static_config::publish_subscribe::StaticConfig; + +use crate::iox2_message_type_details_t; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct iox2_static_config_publish_subscribe_t { + pub max_subscribers: usize, + pub max_publishers: usize, + pub max_nodes: usize, + pub history_size: usize, + pub subscriber_max_buffer_size: usize, + pub subscriber_max_borrowed_samples: usize, + pub enable_safe_overflow: bool, + pub message_type_details: iox2_message_type_details_t, +} + +impl From<&StaticConfig> for iox2_static_config_publish_subscribe_t { + fn from(c: &StaticConfig) -> Self { + Self { + max_subscribers: c.max_subscribers(), + max_publishers: c.max_publishers(), + max_nodes: c.max_nodes(), + history_size: c.history_size(), + subscriber_max_buffer_size: c.subscriber_max_buffer_size(), + subscriber_max_borrowed_samples: c.subscriber_max_borrowed_samples(), + enable_safe_overflow: c.has_safe_overflow(), + message_type_details: c.message_type_details().into(), + } + } +} From 58ca5a2b8b5fea63c86481be0e4853fa28e9835b Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 19:42:51 +0200 Subject: [PATCH 26/35] [#264] Add pubsub tests --- .../src/service_publish_subscribe_tests.cpp | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp index 9ebd0991e..1a9656bf2 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp @@ -246,4 +246,44 @@ TYPED_TEST(ServicePublishSubscribeTest, setting_service_properties_works) { ASSERT_THAT(static_config.message_type_details().payload().alignment(), Eq(alignof(uint64_t))); ASSERT_THAT(static_config.message_type_details().payload().type_name(), StrEq(typeid(uint64_t).name())); } + +TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_publisher_requirement) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name) + .template publish_subscribe() + .max_publishers(11) + .create() + .expect(""); + + auto service_fail = + node.service_builder(service_name).template publish_subscribe().max_publishers(12).open(); + + ASSERT_TRUE(service_fail.has_error()); + ASSERT_THAT(service_fail.error(), Eq(PublishSubscribeOpenError::DoesNotSupportRequestedAmountOfPublishers)); +} + +TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_subscriber_requirement) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name) + .template publish_subscribe() + .max_subscribers(11) + .create() + .expect(""); + + auto service_fail = + node.service_builder(service_name).template publish_subscribe().max_subscribers(12).open(); + + ASSERT_TRUE(service_fail.has_error()); + ASSERT_THAT(service_fail.error(), Eq(PublishSubscribeOpenError::DoesNotSupportRequestedAmountOfSubscribers)); +} } // namespace From bda95d9db85ddb87fe441a52d561c0b22246d5ca Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 20:06:14 +0200 Subject: [PATCH 27/35] [#264] Add C event example --- examples/README.md | 2 +- examples/c/CMakeLists.txt | 1 + examples/c/event/CMakeLists.txt | 22 ++++++ examples/c/event/README.md | 43 ++++++++++++ examples/c/event/src/listener.c | 84 +++++++++++++++++++++++ examples/c/event/src/notifier.c | 92 ++++++++++++++++++++++++++ examples/c/publish_subscribe/README.md | 2 +- 7 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 examples/c/event/CMakeLists.txt create mode 100644 examples/c/event/README.md create mode 100644 examples/c/event/src/listener.c create mode 100644 examples/c/event/src/notifier.c diff --git a/examples/README.md b/examples/README.md index 94cba053e..045e5d5ba 100644 --- a/examples/README.md +++ b/examples/README.md @@ -48,7 +48,7 @@ they interact and exchange data. | complex data types | [Rust](rust/complex_data_types) | Send zero-copy compatible versions of `Vec` and `String`. Introduces `PlacementDefault` trait for large data types to perform an in place initialization where otherwise a stack overflow would be encountered.| | discovery | [Rust](rust/discovery) | List all available services in a system. | | docker | [all](rust/docker) | Communicate between different docker containers and the host. | -| event | [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them.| +| event | [C](c/event) [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them.| | publish subscribe | [C](c/publish_subscribe) [C++](cxx/publish_subscribe) [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | | publish subscribe dynamic data | [Rust](rust/publish_subscribe_dynamic_data) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) and payload data that has a dynamic size. | | publish subscribe with user header | [Rust](rust/publish_subscribe_with_user_header) | Add a user header to the payload (samples) to transfer additional information. | diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 73a6bf378..6b8e3acd9 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -14,4 +14,5 @@ cmake_minimum_required(VERSION 3.22) project(examples_c LANGUAGES C) add_subdirectory(discovery) +add_subdirectory(event) add_subdirectory(publish_subscribe) diff --git a/examples/c/event/CMakeLists.txt b/examples/c/event/CMakeLists.txt new file mode 100644 index 000000000..106303f3d --- /dev/null +++ b/examples/c/event/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2024 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 Software License 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +# which is available at https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cmake_minimum_required(VERSION 3.22) +project(example_c_event LANGUAGES C) + +find_package(iceoryx2-c 0.3.0 REQUIRED) + +add_executable(example_c_event_listener src/listener.c) +target_link_libraries(example_c_event_listener iceoryx2-c::static-lib) + +add_executable(example_c_event_notifier src/notifier.c) +target_link_libraries(example_c_event_notifier iceoryx2-c::static-lib) diff --git a/examples/c/event/README.md b/examples/c/event/README.md new file mode 100644 index 000000000..846ae0092 --- /dev/null +++ b/examples/c/event/README.md @@ -0,0 +1,43 @@ +# Event + +## Running The Example + +This example offers a practical demonstration of inter-process event signaling +in iceoryx2. It showcases how one process can signal an event to another +process, allowing for efficient communication. + +In this scenario, the 'listener' process waits for incoming events. When an +event arrives, it promptly awakens and reports the [`EventId`] of the received +event. On the other end, the 'notifier' process periodically sends notifications +with an incrementing `EventId` every second. + +First you have to build the C examples: + +```sh +cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON +cmake --build target/ffi/build +``` + +To see this in action, open two separate terminals and run the following +commands: + +**Terminal 1** + +```sh +./target/ffi/build/examples/c/event/example_c_event_listener +``` + +**Terminal 2** + +```sh +./target/ffi/build/examples/c/event/example_c_event_notifier +``` + +Feel free to run multiple listeners or notifiers concurrently to observe how +iceoryx2 efficiently handles event signaling across processes. + +You may hit the maximum supported number of ports when too many listener or +notifier processes run. Take a look at the [iceoryx2 config](../../../config) to set the +limits globally or at the +[API of the Service builder](https://docs.rs/iceoryx2/latest/iceoryx2/service/index.html) +to set them for a single service. diff --git a/examples/c/event/src/listener.c b/examples/c/event/src/listener.c new file mode 100644 index 000000000..2720a478a --- /dev/null +++ b/examples/c/event/src/listener.c @@ -0,0 +1,84 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/iceoryx2.h" + +#include +#include +#include +#include + +int main(void) { + // create new node + iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); + iox2_node_h node_handle = NULL; + if (iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle) != IOX2_OK) { + printf("Could not create node!\n"); + goto end; + } + + // create service name + const char* service_name_value = "MyEventName"; + iox2_service_name_h service_name = NULL; + if (iox2_service_name_new(NULL, service_name_value, strlen(service_name_value), &service_name) != IOX2_OK) { + printf("Unable to create service name!\n"); + goto drop_node; + } + + // create service + iox2_service_name_ptr service_name_ptr = iox2_cast_service_name_ptr(service_name); + iox2_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); + iox2_service_builder_event_h service_builder_event = iox2_service_builder_event(service_builder); + iox2_service_builder_event_ref_h service_builder_event_ref = + iox2_cast_service_builder_event_ref_h(service_builder_event); + iox2_port_factory_event_h service = NULL; + if (iox2_service_builder_event_open_or_create(service_builder_event, NULL, &service) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + // create listener + iox2_port_factory_event_ref_h ref_service = iox2_cast_port_factory_event_ref_h(service); + iox2_port_factory_listener_builder_h listener_builder = iox2_port_factory_event_listener_builder(ref_service, NULL); + iox2_listener_h listener = NULL; + if (iox2_port_factory_listener_builder_create(listener_builder, NULL, &listener) != IOX2_OK) { + printf("Unable to create listener!\n"); + goto drop_service; + } + iox2_listener_ref_h listener_ref = iox2_cast_listener_ref_h(listener); + iox2_event_id_t event_id; + + while (iox2_node_wait(node_ref_handle, 0, 0) == iox2_node_event_e_TICK) { + bool has_received_one = false; + if (iox2_listener_timed_wait_one(listener_ref, &event_id, &has_received_one, 1, 0) != IOX2_OK) { + printf("Unable to wait for notification!\n"); + goto drop_listener; + } + + if (has_received_one) { + printf("event was triggered with id: %lu\n", event_id.value); + } + } + +drop_listener: + iox2_listener_drop(listener); + +drop_service: + iox2_port_factory_event_drop(service); + +drop_node: + iox2_node_drop(node_handle); + +end: + return 0; +} diff --git a/examples/c/event/src/notifier.c b/examples/c/event/src/notifier.c new file mode 100644 index 000000000..617e8c710 --- /dev/null +++ b/examples/c/event/src/notifier.c @@ -0,0 +1,92 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/iceoryx2.h" + +#include +#include +#include +#include + +#ifdef _WIN64 +#include +#else +#include +#endif + +int main(void) { + // create new node + iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); + iox2_node_h node_handle = NULL; + if (iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle) != IOX2_OK) { + printf("Could not create node!\n"); + goto end; + } + + // create service name + const char* service_name_value = "MyEventName"; + iox2_service_name_h service_name = NULL; + if (iox2_service_name_new(NULL, service_name_value, strlen(service_name_value), &service_name) != IOX2_OK) { + printf("Unable to create service name!\n"); + goto drop_node; + } + + // create service + iox2_service_name_ptr service_name_ptr = iox2_cast_service_name_ptr(service_name); + iox2_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); + iox2_service_builder_event_h service_builder_event = iox2_service_builder_event(service_builder); + iox2_service_builder_event_ref_h service_builder_event_ref = + iox2_cast_service_builder_event_ref_h(service_builder_event); + iox2_port_factory_event_h service = NULL; + if (iox2_service_builder_event_open_or_create(service_builder_event, NULL, &service) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + // create notifier + iox2_port_factory_event_ref_h ref_service = iox2_cast_port_factory_event_ref_h(service); + iox2_port_factory_notifier_builder_h notifier_builder = iox2_port_factory_event_notifier_builder(ref_service, NULL); + iox2_notifier_h notifier = NULL; + if (iox2_port_factory_notifier_builder_create(notifier_builder, NULL, ¬ifier) != IOX2_OK) { + printf("Unable to create notifier!\n"); + goto drop_service; + } + iox2_notifier_ref_h notifier_ref = iox2_cast_notifier_ref_h(notifier); + iox2_event_id_t event_id; + + uint64_t counter = 0; + while (iox2_node_wait(node_ref_handle, 0, 0) == iox2_node_event_e_TICK) { + counter += 1; + iox2_event_id_t event_id = { .value = counter % 12 }; + if (iox2_notifier_notify_with_custom_event_id(notifier_ref, &event_id, NULL) != IOX2_OK) { + printf("Failed to notify listener!\n"); + goto drop_notifier; + } + + printf("Trigger event with id %lu ...\n", event_id.value); + + sleep(1); + } + +drop_notifier: + iox2_notifier_drop(notifier); + +drop_service: + iox2_port_factory_event_drop(service); + +drop_node: + iox2_node_drop(node_handle); + +end: + return 0; +} diff --git a/examples/c/publish_subscribe/README.md b/examples/c/publish_subscribe/README.md index 07a649514..020a7a3de 100644 --- a/examples/c/publish_subscribe/README.md +++ b/examples/c/publish_subscribe/README.md @@ -9,7 +9,7 @@ receiving end, the subscriber checks for new data every second. The subscriber is printing the sample on the console whenever new data arrives. -First you have to build the C++ examples: +First you have to build the C examples: ```sh cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON From eb03cf68ae011df78a22cca24ca54848556b6d6f Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jul 2024 20:26:28 +0200 Subject: [PATCH 28/35] [#264] Fix clang-tidy warnings --- .clang-tidy | 2 +- examples/c/event/src/listener.c | 2 -- examples/c/event/src/notifier.c | 4 +-- examples/c/publish_subscribe/src/publisher.c | 8 +++--- iceoryx2-ffi/cxx/include/iox/slice.hpp | 4 +-- .../cxx/include/iox2/enum_translation.hpp | 6 ++-- iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 17 ++++++----- iceoryx2-ffi/cxx/include/iox2/sample.hpp | 11 ++++---- iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp | 19 ++++++------- iceoryx2-ffi/cxx/include/iox2/subscriber.hpp | 12 ++++---- iceoryx2-ffi/cxx/src/message_type_details.cpp | 2 +- .../cxx/tests/src/service_event_tests.cpp | 27 ++++++++++++------ .../src/service_publish_subscribe_tests.cpp | 28 ++++++++++++------- 13 files changed, 77 insertions(+), 65 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 37cab666d..da2ab051e 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -52,7 +52,7 @@ CheckOptions: - { key: readability-identifier-naming.MemberCase, value: lower_case } - { key: readability-identifier-naming.ParameterCase, value: lower_case } - { key: readability-identifier-naming.VariableCase, value: lower_case } - - { key: readability-identifier-naming.PublicMemberPrefix, value: } + - { key: readability-identifier-naming.PublicMemberPrefix, value: "" } - { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ } - { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ } - { key: readability-identifier-naming.MacroDefinitionPrefix, value: IOX2_ } diff --git a/examples/c/event/src/listener.c b/examples/c/event/src/listener.c index 2720a478a..4f62035ce 100644 --- a/examples/c/event/src/listener.c +++ b/examples/c/event/src/listener.c @@ -39,8 +39,6 @@ int main(void) { iox2_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); iox2_service_builder_event_h service_builder_event = iox2_service_builder_event(service_builder); - iox2_service_builder_event_ref_h service_builder_event_ref = - iox2_cast_service_builder_event_ref_h(service_builder_event); iox2_port_factory_event_h service = NULL; if (iox2_service_builder_event_open_or_create(service_builder_event, NULL, &service) != IOX2_OK) { printf("Unable to create service!\n"); diff --git a/examples/c/event/src/notifier.c b/examples/c/event/src/notifier.c index 617e8c710..172221cf9 100644 --- a/examples/c/event/src/notifier.c +++ b/examples/c/event/src/notifier.c @@ -45,8 +45,6 @@ int main(void) { iox2_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); iox2_service_builder_event_h service_builder_event = iox2_service_builder_event(service_builder); - iox2_service_builder_event_ref_h service_builder_event_ref = - iox2_cast_service_builder_event_ref_h(service_builder_event); iox2_port_factory_event_h service = NULL; if (iox2_service_builder_event_open_or_create(service_builder_event, NULL, &service) != IOX2_OK) { printf("Unable to create service!\n"); @@ -67,7 +65,7 @@ int main(void) { uint64_t counter = 0; while (iox2_node_wait(node_ref_handle, 0, 0) == iox2_node_event_e_TICK) { counter += 1; - iox2_event_id_t event_id = { .value = counter % 12 }; + iox2_event_id_t event_id = { .value = counter % 12 }; // NOLINT if (iox2_notifier_notify_with_custom_event_id(notifier_ref, &event_id, NULL) != IOX2_OK) { printf("Failed to notify listener!\n"); goto drop_notifier; diff --git a/examples/c/publish_subscribe/src/publisher.c b/examples/c/publish_subscribe/src/publisher.c index 2294f5265..9ce98ffd7 100644 --- a/examples/c/publish_subscribe/src/publisher.c +++ b/examples/c/publish_subscribe/src/publisher.c @@ -72,7 +72,7 @@ int main(void) { } iox2_publisher_ref_h publisher_ref = iox2_cast_publisher_ref_h(publisher); - uint64_t counter = 0; + int32_t counter = 0; while (iox2_node_wait(node_ref_handle, 1, 0) == iox2_node_event_e_TICK) { counter += 1; @@ -85,11 +85,11 @@ int main(void) { iox2_sample_mut_ref_h sample_ref = iox2_cast_sample_mut_ref_h(sample); // write payload - struct TransmissionData* payload; + struct TransmissionData* payload = NULL; iox2_sample_mut_payload_mut(sample_ref, (void**) &payload, NULL); payload->x = counter; payload->y = counter * 3; - payload->funky = counter * 812.12; + payload->funky = counter * 812.12; // NOLINT // send sample if (iox2_sample_mut_send(sample, NULL) != IOX2_OK) { @@ -97,7 +97,7 @@ int main(void) { goto drop_publisher; } - printf("Send sample %lu ...\n", counter); + printf("Send sample %d ...\n", counter); } diff --git a/iceoryx2-ffi/cxx/include/iox/slice.hpp b/iceoryx2-ffi/cxx/include/iox/slice.hpp index c4b2f25b2..c0fcae70a 100644 --- a/iceoryx2-ffi/cxx/include/iox/slice.hpp +++ b/iceoryx2-ffi/cxx/include/iox/slice.hpp @@ -50,12 +50,12 @@ class Slice { template struct IsSlice { - static constexpr bool value = false; + static constexpr bool VALUE = false; }; template struct IsSlice> { - static constexpr bool value = true; + static constexpr bool VALUE = true; }; } // namespace iox diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index 55bb71ef2..261605486 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -503,7 +503,7 @@ constexpr auto from(const int value) noexcept -> case iox2_publisher_send_error_e_LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES: return iox2::PublisherSendError::LoanErrorExceedsMaxLoanedSamples; case iox2_publisher_send_error_e_LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE: - return iox2::PublisherSendError::LoanErrorInternalFailure; + return iox2::PublisherSendError::LoanErrorExceedsMaxLoanSize; case iox2_publisher_send_error_e_LOAN_ERROR_INTERNAL_FAILURE: return iox2::PublisherSendError::LoanErrorInternalFailure; case iox2_publisher_send_error_e_CONNECTION_ERROR: @@ -545,8 +545,8 @@ constexpr auto from(const int value) noexcept -> template <> constexpr auto from(const int value) noexcept -> iox2::TypeVariant { - const auto v = static_cast(value); - switch (v) { + const auto variant = static_cast(value); + switch (variant) { case iox2_type_variant_e_DYNAMIC: return iox2::TypeVariant::Dynamic; case iox2_type_variant_e_FIXED_SIZE: diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 62223384e..5433c80f4 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -30,8 +30,8 @@ namespace iox2 { template class Publisher { public: - Publisher(Publisher&&) noexcept; - auto operator=(Publisher&&) noexcept -> Publisher&; + Publisher(Publisher&& rhs) noexcept; + auto operator=(Publisher&& rhs) noexcept -> Publisher&; ~Publisher(); Publisher(const Publisher&) = delete; @@ -64,14 +64,14 @@ class Publisher { /// [`Payload`]. /// /// On failure it returns [`PublisherLoanError`] describing the failure. - auto loan_slice(const uint64_t number_of_elements) - -> iox::expected, PublisherLoanError>; + auto + loan_slice(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`]. /// The user has to initialize the payload before it can be sent. /// /// On failure it returns [`PublisherLoanError`] describing the failure. - auto loan_slice_uninit(const uint64_t number_of_elements) + auto loan_slice_uninit(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; /// Explicitly updates all connections to the [`Subscriber`]s. This is @@ -89,7 +89,7 @@ class Publisher { explicit Publisher(iox2_publisher_h handle); void drop(); - iox2_publisher_h m_handle; + iox2_publisher_h m_handle { nullptr }; }; template @@ -106,8 +106,7 @@ inline void Publisher::drop() { } template -inline Publisher::Publisher(Publisher&& rhs) noexcept - : m_handle { nullptr } { +inline Publisher::Publisher(Publisher&& rhs) noexcept { *this = std::move(rhs); } @@ -135,7 +134,7 @@ inline auto Publisher::id() const -> UniquePublisherId { template inline auto Publisher::send_copy(const Payload& payload) const -> iox::expected { - static_assert(std::is_trivially_copyable::value, ""); + static_assert(std::is_trivially_copyable::value); auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); diff --git a/iceoryx2-ffi/cxx/include/iox2/sample.hpp b/iceoryx2-ffi/cxx/include/iox2/sample.hpp index c203fbd84..bf07fba12 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample.hpp @@ -34,8 +34,8 @@ namespace iox2 { template class Sample { public: - Sample(Sample&&) noexcept; - auto operator=(Sample&&) noexcept -> Sample&; + Sample(Sample&& rhs) noexcept; + auto operator=(Sample&& rhs) noexcept -> Sample&; ~Sample(); Sample(const Sample&) = delete; @@ -67,7 +67,7 @@ class Sample { explicit Sample(iox2_sample_h handle); void drop(); - iox2_sample_h m_handle; + iox2_sample_h m_handle { nullptr }; }; template @@ -84,8 +84,7 @@ inline void Sample::drop() { } template -inline Sample::Sample(Sample&& rhs) noexcept - : m_handle { nullptr } { +inline Sample::Sample(Sample&& rhs) noexcept { *this = std::move(rhs); } @@ -121,7 +120,9 @@ inline auto Sample::payload() const -> const Payload& { const Payload* payload_ptr = nullptr; size_t payload_len = 0; + // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast), no other way for type erasure iox2_sample_payload(ref_handle, reinterpret_cast(&payload_ptr), &payload_len); + // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) IOX_ASSERT(sizeof(Payload) <= payload_len, ""); return *payload_ptr; diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index 088f8c927..ff984902d 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -49,8 +49,8 @@ namespace iox2 { template class SampleMut { public: - SampleMut(SampleMut&&) noexcept; - auto operator=(SampleMut&&) noexcept -> SampleMut&; + SampleMut(SampleMut&& rhs) noexcept; + auto operator=(SampleMut&& rhs) noexcept -> SampleMut&; ~SampleMut() noexcept; SampleMut(const SampleMut&) = delete; @@ -86,25 +86,25 @@ class SampleMut { auto payload_mut() -> Payload&; /// Writes the payload to the sample - template ::value, T>> + template ::VALUE, T>> void write_payload(T&& value); /// Writes the payload to the sample - template ::value, T>> + template ::VALUE, T>> void write_from_fn(const iox::function& initializer); - protected: + private: template friend class Publisher; - template + template friend auto - send_sample(SampleMut<_S, _Payload, _UserHeader>&& sample) -> iox::expected; + send_sample(SampleMut&& sample) -> iox::expected; explicit SampleMut(iox2_sample_mut_h handle); void drop(); - iox2_sample_mut_h m_handle; + iox2_sample_mut_h m_handle { nullptr }; }; template @@ -121,8 +121,7 @@ inline void SampleMut::drop() { } template -inline SampleMut::SampleMut(SampleMut&& rhs) noexcept - : m_handle { nullptr } { +inline SampleMut::SampleMut(SampleMut&& rhs) noexcept { *this = std::move(rhs); } diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp index 6ca64bde2..19aac5416 100644 --- a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp @@ -28,8 +28,8 @@ namespace iox2 { template class Subscriber { public: - Subscriber(Subscriber&&) noexcept; - auto operator=(Subscriber&&) noexcept -> Subscriber&; + Subscriber(Subscriber&& rhs) noexcept; + auto operator=(Subscriber&& rhs) noexcept -> Subscriber&; ~Subscriber(); Subscriber(const Subscriber&) = delete; @@ -60,7 +60,7 @@ class Subscriber { explicit Subscriber(iox2_subscriber_h handle); void drop(); - iox2_subscriber_h m_handle; + iox2_subscriber_h m_handle { nullptr }; }; template inline Subscriber::Subscriber(iox2_subscriber_h handle) @@ -68,8 +68,7 @@ inline Subscriber::Subscriber(iox2_subscriber_h handle) } template -inline Subscriber::Subscriber(Subscriber&& rhs) noexcept - : m_handle { nullptr } { +inline Subscriber::Subscriber(Subscriber&& rhs) noexcept { *this = std::move(rhs); } @@ -118,9 +117,8 @@ inline auto Subscriber::receive() const if (sample_handle != nullptr) { return iox::ok( iox::optional>(Sample(sample_handle))); - } else { - return iox::ok(iox::optional>(iox::nullopt)); } + return iox::ok(iox::optional>(iox::nullopt)); } return iox::err(iox::into(result)); diff --git a/iceoryx2-ffi/cxx/src/message_type_details.cpp b/iceoryx2-ffi/cxx/src/message_type_details.cpp index 9e41b60ef..933efa08a 100644 --- a/iceoryx2-ffi/cxx/src/message_type_details.cpp +++ b/iceoryx2-ffi/cxx/src/message_type_details.cpp @@ -23,7 +23,7 @@ auto TypeDetail::variant() const -> TypeVariant { } auto TypeDetail::type_name() const -> const char* { - return m_value.type_name; + return &m_value.type_name[0]; } auto TypeDetail::size() const -> size_t { diff --git a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp index 783412385..757a313f2 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp @@ -36,7 +36,8 @@ struct ServiceEventTest : public ::testing::Test { static std::atomic event_id_counter; static constexpr ServiceType TYPE = T::TYPE; - const char* service_name_value; + //NOLINTBEGIN(misc-non-private-member-variables-in-classes), required for tests + const char* service_name_value { nullptr }; ServiceName service_name; Node node; PortFactoryEvent service; @@ -44,6 +45,7 @@ struct ServiceEventTest : public ::testing::Test { Listener listener; EventId event_id_1; EventId event_id_2; + //NOLINTEND(misc-non-private-member-variables-in-classes) }; template @@ -89,28 +91,36 @@ TYPED_TEST(ServiceEventTest, creating_existing_service_fails) { TYPED_TEST(ServiceEventTest, service_settings_are_applied) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_NOTIFIERS = 5; + constexpr uint64_t NUMBER_OF_LISTENERS = 7; const auto* name_value = "First time we met, I saw the ocean, it was wet!"; const auto service_name = ServiceName::create(name_value).expect(""); auto node = NodeBuilder().create().expect(""); - auto sut = node.service_builder(service_name).event().max_notifiers(5).max_listeners(7).create().expect(""); + auto sut = node.service_builder(service_name) + .event() + .max_notifiers(NUMBER_OF_NOTIFIERS) + .max_listeners(NUMBER_OF_LISTENERS) + .create() + .expect(""); auto static_config = sut.static_config(); - ASSERT_THAT(static_config.max_notifiers(), Eq(5)); - ASSERT_THAT(static_config.max_listeners(), Eq(7)); + ASSERT_THAT(static_config.max_notifiers(), Eq(NUMBER_OF_NOTIFIERS)); + ASSERT_THAT(static_config.max_listeners(), Eq(NUMBER_OF_LISTENERS)); } TYPED_TEST(ServiceEventTest, open_fails_with_incompatible_max_notifiers_requirements) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_NOTIFIERS = 5; const auto* name_value = "First time we met, I saw the ocean, it was wet!"; const auto service_name = ServiceName::create(name_value).expect(""); auto node = NodeBuilder().create().expect(""); - auto sut = node.service_builder(service_name).event().max_notifiers(5).create().expect(""); - auto sut_fail = node.service_builder(service_name).event().max_notifiers(6).open(); + auto sut = node.service_builder(service_name).event().max_notifiers(NUMBER_OF_NOTIFIERS).create().expect(""); + auto sut_fail = node.service_builder(service_name).event().max_notifiers(NUMBER_OF_NOTIFIERS + 1).open(); ASSERT_TRUE(sut_fail.has_error()); ASSERT_THAT(sut_fail.error(), Eq(EventOpenError::DoesNotSupportRequestedAmountOfNotifiers)); @@ -118,13 +128,14 @@ TYPED_TEST(ServiceEventTest, open_fails_with_incompatible_max_notifiers_requirem TYPED_TEST(ServiceEventTest, open_fails_with_incompatible_max_listeners_requirements) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_LISTENERS = 7; const auto* name_value = "First time we met, I saw the ocean, it was wet!"; const auto service_name = ServiceName::create(name_value).expect(""); auto node = NodeBuilder().create().expect(""); - auto sut = node.service_builder(service_name).event().max_listeners(5).create().expect(""); - auto sut_fail = node.service_builder(service_name).event().max_listeners(6).open(); + auto sut = node.service_builder(service_name).event().max_listeners(NUMBER_OF_LISTENERS).create().expect(""); + auto sut_fail = node.service_builder(service_name).event().max_listeners(NUMBER_OF_LISTENERS + 1).open(); ASSERT_TRUE(sut_fail.has_error()); ASSERT_THAT(sut_fail.error(), Eq(EventOpenError::DoesNotSupportRequestedAmountOfListeners)); diff --git a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp index 1a9656bf2..4f55e5abc 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp @@ -226,6 +226,8 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_send_receive_works) { TYPED_TEST(ServicePublishSubscribeTest, setting_service_properties_works) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_PUBLISHERS = 11; + constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 12; const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; const auto service_name = ServiceName::create(name_value).expect(""); @@ -233,15 +235,15 @@ TYPED_TEST(ServicePublishSubscribeTest, setting_service_properties_works) { auto node = NodeBuilder().create().expect(""); auto service = node.service_builder(service_name) .template publish_subscribe() - .max_publishers(11) - .max_subscribers(12) + .max_publishers(NUMBER_OF_PUBLISHERS) + .max_subscribers(NUMBER_OF_SUBSCRIBERS) .create() .expect(""); auto static_config = service.static_config(); - ASSERT_THAT(static_config.max_publishers(), Eq(11)); - ASSERT_THAT(static_config.max_subscribers(), Eq(12)); + ASSERT_THAT(static_config.max_publishers(), Eq(NUMBER_OF_PUBLISHERS)); + ASSERT_THAT(static_config.max_subscribers(), Eq(NUMBER_OF_SUBSCRIBERS)); ASSERT_THAT(static_config.message_type_details().payload().size(), Eq(sizeof(uint64_t))); ASSERT_THAT(static_config.message_type_details().payload().alignment(), Eq(alignof(uint64_t))); ASSERT_THAT(static_config.message_type_details().payload().type_name(), StrEq(typeid(uint64_t).name())); @@ -249,6 +251,7 @@ TYPED_TEST(ServicePublishSubscribeTest, setting_service_properties_works) { TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_publisher_requirement) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_PUBLISHERS = 11; const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; const auto service_name = ServiceName::create(name_value).expect(""); @@ -256,12 +259,14 @@ TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_publisher_r auto node = NodeBuilder().create().expect(""); auto service = node.service_builder(service_name) .template publish_subscribe() - .max_publishers(11) + .max_publishers(NUMBER_OF_PUBLISHERS) .create() .expect(""); - auto service_fail = - node.service_builder(service_name).template publish_subscribe().max_publishers(12).open(); + auto service_fail = node.service_builder(service_name) + .template publish_subscribe() + .max_publishers(NUMBER_OF_PUBLISHERS + 1) + .open(); ASSERT_TRUE(service_fail.has_error()); ASSERT_THAT(service_fail.error(), Eq(PublishSubscribeOpenError::DoesNotSupportRequestedAmountOfPublishers)); @@ -269,6 +274,7 @@ TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_publisher_r TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_subscriber_requirement) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 12; const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; const auto service_name = ServiceName::create(name_value).expect(""); @@ -276,12 +282,14 @@ TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_subscriber_ auto node = NodeBuilder().create().expect(""); auto service = node.service_builder(service_name) .template publish_subscribe() - .max_subscribers(11) + .max_subscribers(NUMBER_OF_SUBSCRIBERS) .create() .expect(""); - auto service_fail = - node.service_builder(service_name).template publish_subscribe().max_subscribers(12).open(); + auto service_fail = node.service_builder(service_name) + .template publish_subscribe() + .max_subscribers(NUMBER_OF_SUBSCRIBERS + 1) + .open(); ASSERT_TRUE(service_fail.has_error()); ASSERT_THAT(service_fail.error(), Eq(PublishSubscribeOpenError::DoesNotSupportRequestedAmountOfSubscribers)); From 18d16aa1d7643cfb4de3df45f2b3761a29506330 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sun, 28 Jul 2024 13:12:38 +0200 Subject: [PATCH 29/35] [#264] Fix Mac OS integer conversions --- iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 4 ++-- iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 5433c80f4..be87ed01a 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -43,7 +43,7 @@ class Publisher { /// Copies the input `value` into a [`SampleMut`] and delivers it. /// On success it returns the number of [`Subscriber`]s that received /// the data, otherwise a [`PublisherSendError`] describing the failure. - auto send_copy(const Payload& payload) const -> iox::expected; + auto send_copy(const Payload& payload) const -> iox::expected; /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`]. /// The user has to initialize the payload before it can be sent. @@ -133,7 +133,7 @@ inline auto Publisher::id() const -> UniquePublisherId { template inline auto Publisher::send_copy(const Payload& payload) const - -> iox::expected { + -> iox::expected { static_assert(std::is_trivially_copyable::value); auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index ff984902d..3aeb29327 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -98,8 +98,7 @@ class SampleMut { friend class Publisher; template - friend auto - send_sample(SampleMut&& sample) -> iox::expected; + friend auto send_sample(SampleMut&& sample) -> iox::expected; explicit SampleMut(iox2_sample_mut_h handle); void drop(); @@ -216,7 +215,7 @@ SampleMut::write_from_fn(const iox::function -inline auto send_sample(SampleMut&& sample) -> iox::expected { +inline auto send_sample(SampleMut&& sample) -> iox::expected { size_t number_of_recipients = 0; auto result = iox2_sample_mut_send(sample.m_handle, &number_of_recipients); sample.m_handle = nullptr; From 1cfaca8653a5710880e7c1ab669c59a3e16d4f17 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sun, 28 Jul 2024 08:13:32 -0700 Subject: [PATCH 30/35] [#264] Fix windows build --- examples/c/event/src/listener.c | 1 - examples/c/event/src/notifier.c | 4 ++-- examples/c/publish_subscribe/src/publisher.c | 4 ++++ examples/c/publish_subscribe/src/subscriber.c | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/c/event/src/listener.c b/examples/c/event/src/listener.c index 4f62035ce..88e830ac6 100644 --- a/examples/c/event/src/listener.c +++ b/examples/c/event/src/listener.c @@ -12,7 +12,6 @@ #include "iox2/iceoryx2.h" -#include #include #include #include diff --git a/examples/c/event/src/notifier.c b/examples/c/event/src/notifier.c index 172221cf9..73ea6733f 100644 --- a/examples/c/event/src/notifier.c +++ b/examples/c/event/src/notifier.c @@ -12,13 +12,13 @@ #include "iox2/iceoryx2.h" -#include #include #include #include #ifdef _WIN64 #include +#define sleep Sleep #else #include #endif @@ -71,7 +71,7 @@ int main(void) { goto drop_notifier; } - printf("Trigger event with id %lu ...\n", event_id.value); + printf("Trigger event with id %lu ...\n", (long unsigned) event_id.value); sleep(1); } diff --git a/examples/c/publish_subscribe/src/publisher.c b/examples/c/publish_subscribe/src/publisher.c index 9ce98ffd7..96ae9fecb 100644 --- a/examples/c/publish_subscribe/src/publisher.c +++ b/examples/c/publish_subscribe/src/publisher.c @@ -13,7 +13,11 @@ #include "iox2/iceoryx2.h" #include "transmission_data.h" +#ifdef _WIN64 +#define alignof __alignof +#else #include +#endif #include #include #include diff --git a/examples/c/publish_subscribe/src/subscriber.c b/examples/c/publish_subscribe/src/subscriber.c index e98c99597..7a2234e93 100644 --- a/examples/c/publish_subscribe/src/subscriber.c +++ b/examples/c/publish_subscribe/src/subscriber.c @@ -13,7 +13,11 @@ #include "iox2/iceoryx2.h" #include "transmission_data.h" +#ifdef _WIN64 +#define alignof __alignof +#else #include +#endif #include #include #include From ca12b6217aad78413f88e08529dbda9d2acafbb7 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Mon, 29 Jul 2024 18:50:16 +0200 Subject: [PATCH 31/35] [#264] Store payload size in publisher --- iceoryx2/src/port/publisher.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/iceoryx2/src/port/publisher.rs b/iceoryx2/src/port/publisher.rs index 9768b074b..e2e1684d0 100644 --- a/iceoryx2/src/port/publisher.rs +++ b/iceoryx2/src/port/publisher.rs @@ -534,6 +534,7 @@ impl DataSegment { pub struct Publisher { pub(crate) data_segment: Arc>, dynamic_publisher_handle: Option, + payload_size: usize, _payload: PhantomData, _user_header: PhantomData, } @@ -619,9 +620,17 @@ impl loan_counter: IoxAtomicUsize::new(0), }); + let payload_size = data_segment + .subscriber_connections + .static_config + .message_type_details + .payload + .size; + let mut new_self = Self { data_segment, dynamic_publisher_handle: None, + payload_size, _payload: PhantomData, _user_header: PhantomData, }; @@ -983,16 +992,8 @@ impl )) }; - let payload_size = self - .data_segment - .subscriber_connections - .static_config - .message_type_details - .payload - .size; - let slice_len_adjusted_to_payload_type_details = - payload_size * slice_len / core::mem::size_of::(); + self.payload_size * slice_len / core::mem::size_of::(); let sample = unsafe { RawSampleMut::new_unchecked( From b79059449895adbf43c7c67696b7b39c0c4fdab2 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Tue, 30 Jul 2024 23:54:43 +0200 Subject: [PATCH 32/35] [#264] Enable slice methods only for slice Payload; remove reinterpret cast --- iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 5 +++++ iceoryx2-ffi/cxx/include/iox2/sample.hpp | 8 +++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index be87ed01a..4c37e397d 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -24,6 +24,7 @@ #include "iox2/unique_port_id.hpp" #include +#include namespace iox2 { /// Sending endpoint of a publish-subscriber based communication. @@ -64,6 +65,7 @@ class Publisher { /// [`Payload`]. /// /// On failure it returns [`PublisherLoanError`] describing the failure. + template ::VALUE, T>> auto loan_slice(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; @@ -71,6 +73,7 @@ class Publisher { /// The user has to initialize the payload before it can be sent. /// /// On failure it returns [`PublisherLoanError`] describing the failure. + template ::VALUE, T>> auto loan_slice_uninit(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; @@ -171,12 +174,14 @@ Publisher::loan() -> iox::expected +template inline auto Publisher::loan_slice(const uint64_t number_of_elements) -> iox::expected, PublisherLoanError> { IOX_TODO(); } template +template inline auto Publisher::loan_slice_uninit(const uint64_t number_of_elements) -> iox::expected, PublisherLoanError> { IOX_TODO(); diff --git a/iceoryx2-ffi/cxx/include/iox2/sample.hpp b/iceoryx2-ffi/cxx/include/iox2/sample.hpp index bf07fba12..013a3e685 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample.hpp @@ -117,15 +117,13 @@ inline auto Sample::operator->() const -> const Payload* template inline auto Sample::payload() const -> const Payload& { auto* ref_handle = iox2_cast_sample_ref_h(m_handle); - const Payload* payload_ptr = nullptr; + const void* payload_ptr = nullptr; size_t payload_len = 0; - // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast), no other way for type erasure - iox2_sample_payload(ref_handle, reinterpret_cast(&payload_ptr), &payload_len); - // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) + iox2_sample_payload(ref_handle, &payload_ptr, &payload_len); IOX_ASSERT(sizeof(Payload) <= payload_len, ""); - return *payload_ptr; + return *static_cast(payload_ptr); } template From c21127da751b398fbc903c76e14b4847098d8a59 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 31 Jul 2024 12:08:21 +0200 Subject: [PATCH 33/35] [#264] Fix windows compilation issue --- iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 4c37e397d..2cad380b1 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -65,17 +65,16 @@ class Publisher { /// [`Payload`]. /// /// On failure it returns [`PublisherLoanError`] describing the failure. - template ::VALUE, T>> - auto - loan_slice(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; + template ::VALUE, void>> + auto loan_slice(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`]. /// The user has to initialize the payload before it can be sent. /// /// On failure it returns [`PublisherLoanError`] describing the failure. - template ::VALUE, T>> - auto loan_slice_uninit(uint64_t number_of_elements) - -> iox::expected, PublisherLoanError>; + template ::VALUE, void>> + auto + loan_slice_uninit(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; /// Explicitly updates all connections to the [`Subscriber`]s. This is /// required to be called whenever a new [`Subscriber`] is connected to @@ -174,16 +173,16 @@ Publisher::loan() -> iox::expected -template +template inline auto Publisher::loan_slice(const uint64_t number_of_elements) - -> iox::expected, PublisherLoanError> { + -> iox::expected, PublisherLoanError> { IOX_TODO(); } template -template +template inline auto Publisher::loan_slice_uninit(const uint64_t number_of_elements) - -> iox::expected, PublisherLoanError> { + -> iox::expected, PublisherLoanError> { IOX_TODO(); } From 7fb599c336b9f703d54495721d8ca2db4b042b17 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 31 Jul 2024 12:31:44 +0200 Subject: [PATCH 34/35] [#264] C loan API no longer requires number of elements to make it more rust similar; fix memory leak on loan fail in publisher --- examples/c/publish_subscribe/src/publisher.c | 2 +- iceoryx2-ffi/cxx/include/iox2/publisher.hpp | 2 +- iceoryx2-ffi/ffi/src/api/publisher.rs | 41 ++++++++------------ 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/examples/c/publish_subscribe/src/publisher.c b/examples/c/publish_subscribe/src/publisher.c index 96ae9fecb..b7ff7aeb3 100644 --- a/examples/c/publish_subscribe/src/publisher.c +++ b/examples/c/publish_subscribe/src/publisher.c @@ -82,7 +82,7 @@ int main(void) { // loan sample iox2_sample_mut_h sample = NULL; - if (iox2_publisher_loan(publisher_ref, 1, NULL, &sample) != IOX2_OK) { + if (iox2_publisher_loan(publisher_ref, NULL, &sample) != IOX2_OK) { printf("Failed to loan sample\n"); goto drop_publisher; } diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 2cad380b1..35de7e6bd 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -157,7 +157,7 @@ inline auto Publisher::loan_uninit() auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); iox2_sample_mut_h sample_handle {}; - auto result = iox2_publisher_loan(ref_handle, 1, nullptr, &sample_handle); + auto result = iox2_publisher_loan(ref_handle, nullptr, &sample_handle); if (result == IOX2_OK) { return iox::ok(SampleMut(sample_handle)); diff --git a/iceoryx2-ffi/ffi/src/api/publisher.rs b/iceoryx2-ffi/ffi/src/api/publisher.rs index cf342b7e7..6c4763a6d 100644 --- a/iceoryx2-ffi/ffi/src/api/publisher.rs +++ b/iceoryx2-ffi/ffi/src/api/publisher.rs @@ -20,7 +20,7 @@ use iceoryx2::prelude::*; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; -use super::{c_size_t, iox2_sample_mut_h, iox2_sample_mut_t, IntoCInt}; +use super::{iox2_sample_mut_h, iox2_sample_mut_t, IntoCInt}; use core::ffi::{c_int, c_void}; use core::mem::ManuallyDrop; @@ -274,8 +274,6 @@ pub unsafe extern "C" fn iox2_publisher_send_copy( /// # Arguments /// /// * `handle` obtained by [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create) -/// * `number_of_elements` defines the number of elements that shall be loaned. The elements were -/// defined via [`iox2_service_builder_pub_sub_set_payload_type_details()`](crate::iox2_service_builder_pub_sub_set_payload_type_details). /// * `sample_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_sample_mut_t`]. /// If it is a NULL pointer, the storage will be allocated on the heap. /// * `sample_handle_ptr` - An uninitialized or dangling [`iox2_sample_mut_h`] handle which will be initialized by this function call if a sample is obtained, otherwise it will be set to NULL. @@ -289,7 +287,6 @@ pub unsafe extern "C" fn iox2_publisher_send_copy( #[no_mangle] pub unsafe extern "C" fn iox2_publisher_loan( publisher_handle: iox2_publisher_ref_h, - number_of_elements: c_size_t, sample_struct_ptr: *mut iox2_sample_mut_t, sample_handle_ptr: *mut iox2_sample_mut_h, ) -> c_int { @@ -298,25 +295,25 @@ pub unsafe extern "C" fn iox2_publisher_loan( *sample_handle_ptr = std::ptr::null_mut(); - let mut sample_struct_ptr = sample_struct_ptr; - fn no_op(_: *mut iox2_sample_mut_t) {} - let mut deleter: fn(*mut iox2_sample_mut_t) = no_op; - if sample_struct_ptr.is_null() { - sample_struct_ptr = iox2_sample_mut_t::alloc(); - deleter = iox2_sample_mut_t::dealloc; - } - debug_assert!(!sample_struct_ptr.is_null()); + let init_sample_struct_ptr = |sample_struct_ptr: *mut iox2_sample_mut_t| { + let mut sample_struct_ptr = sample_struct_ptr; + fn no_op(_: *mut iox2_sample_mut_t) {} + let mut deleter: fn(*mut iox2_sample_mut_t) = no_op; + if sample_struct_ptr.is_null() { + sample_struct_ptr = iox2_sample_mut_t::alloc(); + deleter = iox2_sample_mut_t::dealloc; + } + debug_assert!(!sample_struct_ptr.is_null()); + + (sample_struct_ptr, deleter) + }; let publisher = &mut *publisher_handle.as_type(); match publisher.service_type { - iox2_service_type_e::IPC => match publisher - .value - .as_ref() - .ipc - .loan_slice_uninit(number_of_elements) - { + iox2_service_type_e::IPC => match publisher.value.as_ref().ipc.loan_slice_uninit(1) { Ok(sample) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); (*sample_struct_ptr).init( publisher.service_type, SampleMutUnion::new_ipc(sample), @@ -327,13 +324,9 @@ pub unsafe extern "C" fn iox2_publisher_loan( } Err(error) => error.into_c_int(), }, - iox2_service_type_e::LOCAL => match publisher - .value - .as_ref() - .local - .loan_slice_uninit(number_of_elements) - { + iox2_service_type_e::LOCAL => match publisher.value.as_ref().local.loan_slice_uninit(1) { Ok(sample) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); (*sample_struct_ptr).init( publisher.service_type, SampleMutUnion::new_local(sample), From 28870cbeb10560716a96f5f33d25dcf06bfb92da Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 31 Jul 2024 12:33:18 +0200 Subject: [PATCH 35/35] [#264] Remove potential double free in sample mut --- iceoryx2-ffi/ffi/src/api/sample_mut.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/iceoryx2-ffi/ffi/src/api/sample_mut.rs b/iceoryx2-ffi/ffi/src/api/sample_mut.rs index 4d6fd34dd..3d30a2d4e 100644 --- a/iceoryx2-ffi/ffi/src/api/sample_mut.rs +++ b/iceoryx2-ffi/ffi/src/api/sample_mut.rs @@ -216,7 +216,6 @@ pub unsafe extern "C" fn iox2_sample_mut_send( } } Err(e) => { - (sample_struct.deleter)(sample_struct); return e.into_c_int(); } } @@ -230,7 +229,6 @@ pub unsafe extern "C" fn iox2_sample_mut_send( } } Err(e) => { - (sample_struct.deleter)(sample_struct); return e.into_c_int(); } }