iceoryx2_ffi/api/
node.rs

1// Copyright (c) 2024 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13#![allow(non_camel_case_types)]
14
15use crate::api::{
16    iox2_callback_context, iox2_callback_progression_e, iox2_config_ptr, iox2_node_name_ptr,
17    iox2_service_builder_h, iox2_service_builder_t, iox2_service_name_ptr, iox2_service_type_e,
18    AssertNonNullHandle, HandleToType, IntoCInt, ServiceBuilderUnion, IOX2_OK,
19};
20
21use iceoryx2::node::{NodeId, NodeListFailure, NodeView};
22use iceoryx2::prelude::*;
23use iceoryx2_bb_elementary::static_assert::*;
24use iceoryx2_ffi_macros::iceoryx2_ffi;
25
26use core::ffi::c_int;
27use core::mem::ManuallyDrop;
28use std::time::Duration;
29
30// BEGIN type definition
31
32#[repr(C)]
33#[derive(Copy, Clone)]
34pub enum iox2_node_list_failure_e {
35    INSUFFICIENT_PERMISSIONS = IOX2_OK as isize + 1,
36    INTERRUPT,
37    INTERNAL_ERROR,
38}
39
40impl IntoCInt for NodeListFailure {
41    fn into_c_int(self) -> c_int {
42        (match self {
43            NodeListFailure::InsufficientPermissions => {
44                iox2_node_list_failure_e::INSUFFICIENT_PERMISSIONS
45            }
46            NodeListFailure::Interrupt => iox2_node_list_failure_e::INTERRUPT,
47            NodeListFailure::InternalError => iox2_node_list_failure_e::INTERNAL_ERROR,
48        }) as c_int
49    }
50}
51
52#[repr(C)]
53#[derive(Copy, Clone)]
54pub enum iox2_node_event_e {
55    TICK = 0,
56    TERMINATION_REQUEST,
57    INTERRUPT_SIGNAL,
58}
59
60impl IntoCInt for NodeEvent {
61    fn into_c_int(self) -> c_int {
62        (match self {
63            NodeEvent::Tick => iox2_node_event_e::TICK,
64            NodeEvent::TerminationRequest => iox2_node_event_e::TERMINATION_REQUEST,
65            NodeEvent::InterruptSignal => iox2_node_event_e::INTERRUPT_SIGNAL,
66        }) as c_int
67    }
68}
69
70pub(super) union NodeUnion {
71    ipc: ManuallyDrop<Node<ipc::Service>>,
72    local: ManuallyDrop<Node<local::Service>>,
73}
74
75impl NodeUnion {
76    pub(super) fn new_ipc(node: Node<ipc::Service>) -> Self {
77        Self {
78            ipc: ManuallyDrop::new(node),
79        }
80    }
81    pub(super) fn new_local(node: Node<local::Service>) -> Self {
82        Self {
83            local: ManuallyDrop::new(node),
84        }
85    }
86}
87
88#[repr(C)]
89#[repr(align(8))] // alignment of Option<NodeUnion>
90pub struct iox2_node_storage_t {
91    internal: [u8; 16], // magic number obtained with size_of::<Option<NodeUnion>>()
92}
93
94#[repr(C)]
95#[iceoryx2_ffi(NodeUnion)]
96pub struct iox2_node_t {
97    pub(super) service_type: iox2_service_type_e,
98    pub(super) value: iox2_node_storage_t,
99    pub(super) deleter: fn(*mut iox2_node_t),
100}
101
102impl iox2_node_t {
103    pub(super) fn init(
104        &mut self,
105        service_type: iox2_service_type_e,
106        value: NodeUnion,
107        deleter: fn(*mut iox2_node_t),
108    ) {
109        self.service_type = service_type;
110        self.value.init(value);
111        self.deleter = deleter;
112    }
113}
114
115pub struct iox2_name_h_t;
116/// The owning handle for `iox2_node_t`. Passing the handle to an function transfers the ownership.
117pub type iox2_node_h = *mut iox2_name_h_t;
118/// The non-owning handle for `iox2_node_t`. Passing the handle to an function does not transfers the ownership.
119pub type iox2_node_h_ref = *const iox2_node_h;
120
121impl AssertNonNullHandle for iox2_node_h {
122    fn assert_non_null(self) {
123        debug_assert!(!self.is_null());
124    }
125}
126
127impl AssertNonNullHandle for iox2_node_h_ref {
128    fn assert_non_null(self) {
129        debug_assert!(!self.is_null());
130        unsafe {
131            debug_assert!(!(*self).is_null());
132        }
133    }
134}
135
136impl HandleToType for iox2_node_h {
137    type Target = *mut iox2_node_t;
138
139    fn as_type(self) -> Self::Target {
140        self as *mut _ as _
141    }
142}
143
144impl HandleToType for iox2_node_h_ref {
145    type Target = *mut iox2_node_t;
146
147    fn as_type(self) -> Self::Target {
148        unsafe { *self as *mut _ as _ }
149    }
150}
151
152#[repr(C)]
153#[derive(Copy, Clone)]
154pub enum iox2_node_state_e {
155    ALIVE,
156    DEAD,
157    INACCESSIBLE,
158    UNDEFINED,
159}
160
161// NOTE check the README.md for using opaque types with renaming
162/// The immutable pointer to the underlying `NodeId`
163pub type iox2_node_id_ptr = *const NodeId;
164/// The mutable pointer to the underlying `NodeId`
165pub type iox2_node_id_ptr_mut = *mut NodeId;
166
167/// The callback for [`iox2_node_list`]
168///
169/// # Arguments
170///
171/// * [`iox2_node_state_e`]
172/// * [`iox2_node_id_ptr`]
173/// * [`iox2_node_name_ptr`](crate::iox2_node_name_ptr) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED`
174/// * [`iox2_config_ptr`](crate::iox2_config_ptr) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED`
175/// * [`iox2_callback_context`] -> provided by the user to [`iox2_node_list`] and can be `NULL`
176///
177/// Returns a [`iox2_callback_progression_e`](crate::iox2_callback_progression_e)
178pub type iox2_node_list_callback = extern "C" fn(
179    iox2_node_state_e,
180    iox2_node_id_ptr,
181    iox2_node_name_ptr,
182    iox2_config_ptr,
183    iox2_callback_context,
184) -> iox2_callback_progression_e;
185
186// END type definition
187
188// BEGIN C API
189
190/// Returns the [`iox2_node_name_ptr`](crate::iox2_node_name_ptr), an immutable pointer to the node name.
191///
192/// # Safety
193///
194/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)!
195#[no_mangle]
196pub unsafe extern "C" fn iox2_node_name(node_handle: iox2_node_h_ref) -> iox2_node_name_ptr {
197    node_handle.assert_non_null();
198
199    let node = &mut *node_handle.as_type();
200
201    match node.service_type {
202        iox2_service_type_e::IPC => node.value.as_ref().ipc.name(),
203        iox2_service_type_e::LOCAL => node.value.as_ref().local.name(),
204    }
205}
206
207/// Wait until the provided cycle time has passed and returns a [`iox2_node_event_e`] enum containing the event that
208/// has occurred.
209///
210/// # Safety
211///
212/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)!
213#[no_mangle]
214pub unsafe extern "C" fn iox2_node_wait(
215    node_handle: iox2_node_h_ref,
216    cycle_time_sec: u64,
217    cycle_time_nsec: u32,
218) -> c_int {
219    node_handle.assert_non_null();
220
221    let node = &mut *node_handle.as_type();
222    let cycle_time =
223        Duration::from_secs(cycle_time_sec) + Duration::from_nanos(cycle_time_nsec as u64);
224    match node.service_type {
225        iox2_service_type_e::IPC => node.value.as_ref().ipc.wait(cycle_time),
226        iox2_service_type_e::LOCAL => node.value.as_ref().local.wait(cycle_time),
227    }
228    .into_c_int()
229}
230
231/// Returns the [`iox2_config_ptr`](crate::iox2_config_ptr), an immutable pointer to the config.
232///
233/// # Safety
234///
235/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)!
236#[no_mangle]
237pub unsafe extern "C" fn iox2_node_config(node_handle: iox2_node_h_ref) -> iox2_config_ptr {
238    node_handle.assert_non_null();
239
240    let node = &mut *node_handle.as_type();
241
242    match node.service_type {
243        iox2_service_type_e::IPC => node.value.as_ref().ipc.config(),
244        iox2_service_type_e::LOCAL => node.value.as_ref().local.config(),
245    }
246}
247
248/// Returns the [`iox2_node_id_ptr`](crate::iox2_node_id_ptr), an immutable pointer to the node id.
249///
250/// # Safety
251///
252/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)!
253#[no_mangle]
254pub unsafe extern "C" fn iox2_node_id(node_handle: iox2_node_h_ref) -> iox2_node_id_ptr {
255    node_handle.assert_non_null();
256    todo!() // TODO: [#210] implement
257}
258
259fn iox2_node_list_impl<S: Service>(
260    node_state: &NodeState<S>,
261    callback: iox2_node_list_callback,
262    callback_ctx: iox2_callback_context,
263) -> CallbackProgression {
264    match node_state {
265        NodeState::Alive(alive_node_view) => {
266            let (node_name, config) = alive_node_view
267                .details()
268                .as_ref()
269                .map(|view| (view.name() as _, view.config() as _))
270                .unwrap_or((std::ptr::null(), std::ptr::null()));
271            callback(
272                iox2_node_state_e::ALIVE,
273                alive_node_view.id(),
274                node_name,
275                config,
276                callback_ctx,
277            )
278            .into()
279        }
280        NodeState::Dead(dead_node_view) => {
281            let (node_name, config) = dead_node_view
282                .details()
283                .as_ref()
284                .map(|view| (view.name() as _, view.config() as _))
285                .unwrap_or((std::ptr::null(), std::ptr::null()));
286            callback(
287                iox2_node_state_e::DEAD,
288                dead_node_view.id(),
289                node_name,
290                config,
291                callback_ctx,
292            )
293            .into()
294        }
295        NodeState::Inaccessible(ref node_id) => callback(
296            iox2_node_state_e::INACCESSIBLE,
297            node_id,
298            std::ptr::null(),
299            std::ptr::null(),
300            callback_ctx,
301        )
302        .into(),
303        NodeState::Undefined(ref node_id) => callback(
304            iox2_node_state_e::UNDEFINED,
305            node_id,
306            std::ptr::null(),
307            std::ptr::null(),
308            callback_ctx,
309        )
310        .into(),
311    }
312}
313
314/// Calls the callback repeatedly with an [`iox2_node_state_e`], [`iox2_node_id_ptr`], [´iox2_node_name_ptr´] and [`iox2_config_ptr`] for
315/// all [`Node`](iceoryx2::node::Node)s in the system under a given [`Config`](iceoryx2::config::Config).
316///
317/// # Arguments
318///
319/// * `service_type` - A [`iox2_service_type_e`]
320/// * `config_ptr` - A valid [`iox2_config_ptr`](crate::iox2_config_ptr)
321/// * `callback` - A valid callback with [`iox2_node_list_callback`} signature
322/// * `callback_ctx` - An optional callback context [`iox2_callback_context`} to e.g. store information across callback iterations
323///
324/// Returns IOX2_OK on success, an [`iox2_node_list_failure_e`] otherwise.
325///
326/// # Safety
327///
328/// * The `config_ptr` must be valid and obtained by ether [`iox2_node_config`] or [`iox2_config_global_config`](crate::iox2_config_global_config)!
329#[no_mangle]
330pub unsafe extern "C" fn iox2_node_list(
331    service_type: iox2_service_type_e,
332    config_ptr: iox2_config_ptr,
333    callback: iox2_node_list_callback,
334    callback_ctx: iox2_callback_context,
335) -> c_int {
336    debug_assert!(!config_ptr.is_null());
337
338    let config = &*config_ptr;
339
340    let list_result = match service_type {
341        iox2_service_type_e::IPC => Node::<ipc::Service>::list(config, |node_state| {
342            iox2_node_list_impl(&node_state, callback, callback_ctx)
343        }),
344        iox2_service_type_e::LOCAL => Node::<local::Service>::list(config, |node_state| {
345            iox2_node_list_impl(&node_state, callback, callback_ctx)
346        }),
347    };
348
349    match list_result {
350        Ok(_) => IOX2_OK,
351        Err(e) => e.into_c_int(),
352    }
353}
354
355/// Instantiates a [`iox2_service_builder_h`] for a service with the provided name.
356///
357/// # Arguments
358///
359/// * `node_handle` - Must be a valid [`iox2_node_h_ref`] obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)
360/// * `service_builder_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_service_builder_t`].
361///   If it is a NULL pointer, the storage will be allocated on the heap.
362/// * `service_name_ptr` - Must be a valid [`iox2_service_name_ptr`] obtained by [`iox2_service_name_new`](crate::iox2_service_name_new)
363///   and casted by [`iox2_cast_service_name_ptr`](crate::iox2_cast_service_name_ptr)
364///
365/// Returns the `iox2_service_builder_h` handle for the service builder.
366///
367/// # Safety
368///
369/// * The `node_handle` is still valid after the return of this function and can be use in another function call.
370#[no_mangle]
371pub unsafe extern "C" fn iox2_node_service_builder(
372    node_handle: iox2_node_h_ref,
373    service_builder_struct_ptr: *mut iox2_service_builder_t,
374    service_name_ptr: iox2_service_name_ptr,
375) -> iox2_service_builder_h {
376    node_handle.assert_non_null();
377    debug_assert!(!service_name_ptr.is_null());
378
379    let mut service_builder_struct_ptr = service_builder_struct_ptr;
380    fn no_op(_: *mut iox2_service_builder_t) {}
381    let mut deleter: fn(*mut iox2_service_builder_t) = no_op;
382    if service_builder_struct_ptr.is_null() {
383        service_builder_struct_ptr = iox2_service_builder_t::alloc();
384        deleter = iox2_service_builder_t::dealloc;
385    }
386    debug_assert!(!service_builder_struct_ptr.is_null());
387
388    let node = &mut *node_handle.as_type();
389    match node.service_type {
390        iox2_service_type_e::IPC => {
391            let service_builder = node.value.as_ref().ipc.service_builder(&*service_name_ptr);
392            (*service_builder_struct_ptr).init(
393                node.service_type,
394                ServiceBuilderUnion::new_ipc_base(service_builder),
395                deleter,
396            );
397        }
398        iox2_service_type_e::LOCAL => {
399            let service_builder = node
400                .value
401                .as_ref()
402                .local
403                .service_builder(&*service_name_ptr);
404            (*service_builder_struct_ptr).init(
405                node.service_type,
406                ServiceBuilderUnion::new_local_base(service_builder),
407                deleter,
408            );
409        }
410    };
411
412    (*service_builder_struct_ptr).as_handle()
413}
414
415/// This function needs to be called to destroy the node!
416///
417/// # Arguments
418///
419/// * `node_handle` - A valid [`iox2_node_h`]
420///
421/// # Safety
422///
423/// * The `node_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call!
424/// * The corresponding [`iox2_node_t`] can be re-used with a call to [`iox2_node_builder_create`](crate::iox2_node_builder_create)!
425#[no_mangle]
426pub unsafe extern "C" fn iox2_node_drop(node_handle: iox2_node_h) {
427    node_handle.assert_non_null();
428
429    let node = &mut *node_handle.as_type();
430
431    match node.service_type {
432        iox2_service_type_e::IPC => {
433            ManuallyDrop::drop(&mut node.value.as_mut().ipc);
434        }
435        iox2_service_type_e::LOCAL => {
436            ManuallyDrop::drop(&mut node.value.as_mut().local);
437        }
438    }
439    (node.deleter)(node);
440}
441
442// END C API