Skip to content

Commit

Permalink
[#264] Add c documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
elfenpiff committed Jul 27, 2024
1 parent b3b2bfe commit a9022f0
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 159 deletions.
23 changes: 0 additions & 23 deletions examples/cxx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- TODO -->
```bash
target/ffi/build/examples/cxx/publish_subscribe/example_cxx_publisher
```

<!-- TODO -->
```bash
target/ffi/build/examples/cxx/publish_subscribe/example_cxx_subscriber
```

### Discovery

<!-- TODO -->
```bash
target/ffi/build/examples/cxx/discovery/example_cxx_discovery
```
42 changes: 42 additions & 0 deletions examples/cxx/publish_subscribe/README.md
Original file line number Diff line number Diff line change
@@ -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.
121 changes: 48 additions & 73 deletions iceoryx2-ffi/ffi/src/api/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -200,6 +202,18 @@ unsafe fn send_copy<S: Service>(

// 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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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::<zero_copy::Service>();

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::<u64>()
.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
}
19 changes: 19 additions & 0 deletions iceoryx2-ffi/ffi/src/api/sample.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
42 changes: 39 additions & 3 deletions iceoryx2-ffi/ffi/src/api/sample_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand Down
Loading

0 comments on commit a9022f0

Please sign in to comment.