diff --git a/MODULE.bazel b/MODULE.bazel index abd7b8f53..9d3ab8a88 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -180,7 +180,7 @@ bazel_dep(name = "rules_rust", version = "0.68.1-score") bazel_dep(name = "buildifier_prebuilt", version = "8.2.1.2", dev_dependency = True) -bazel_dep(name = "score_crates", version = "0.0.7", repo_name = "score_communication_crate_index") +bazel_dep(name = "score_crates", version = "0.0.9", repo_name = "score_communication_crate_index") bazel_dep(name = "boost.program_options", version = "1.87.0") bazel_dep(name = "boost.interprocess", version = "1.87.0") diff --git a/score/mw/com/example/com-api-example/basic-consumer-producer.rs b/score/mw/com/example/com-api-example/basic-consumer-producer.rs index 1c2d60b39..cbed79435 100644 --- a/score/mw/com/example/com-api-example/basic-consumer-producer.rs +++ b/score/mw/com/example/com-api-example/basic-consumer-producer.rs @@ -11,11 +11,18 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +// This demo app writing and reading tire pressure data using producer and consumer respectively. +// It is demonstrating the composition of consumer and producer in one struct, +// but they can be used separately as well. +// The example is using Lola runtime, but it can be used with any runtime by changing the runtime initialization part. +// Note: The example is using unwrap and panic in some places for simplicity, +// but it is recommended to handle errors properly in production code. + use clap::Parser; use std::path::PathBuf; use com_api::{ - Builder, Error, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, + Builder, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl, OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, SampleContainer, SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, }; @@ -88,7 +95,7 @@ impl VehicleMonitor { let mut sample_buf = SampleContainer::new(3); match self.tire_subscriber.try_receive(&mut sample_buf, 1) { - Ok(0) => Err(Error::Fail), + Ok(0) => Ok("No tire data received".to_string()), Ok(x) => { let sample = sample_buf.pop_front().unwrap(); Ok(format!("{} samples received: sample[0] = {:?}", x, *sample)) @@ -119,16 +126,20 @@ impl VehicleMonitor { fn create_consumer(runtime: &R, service_id: InstanceSpecifier) -> VehicleConsumer { let consumer_discovery = runtime.find_service::(FindServiceSpecifier::Specific(service_id)); - let available_service_instances = consumer_discovery.get_available_instances().unwrap(); + let available_service_instances = consumer_discovery + .get_available_instances() + .expect("Failed to get available service instances"); // Select service instance at specific handle_index let handle_index = 0; // or any index you need from vector of instances let consumer_builder = available_service_instances .into_iter() .nth(handle_index) - .unwrap(); + .expect("Failed to get consumer builder at specified handle index"); - consumer_builder.build().unwrap() + consumer_builder + .build() + .expect("Failed to build consumer instance") } #[allow(dead_code)] @@ -142,16 +153,16 @@ async fn create_consumer_async( let available_service_instances = consumer_discovery .get_available_instances_async() .await - .unwrap(); + .expect("Failed to get available service instances asynchronously"); // Select service instance at specific handle_index let handle_index = 0; // or any index you need from vector of instances let consumer_builder = available_service_instances .into_iter() .nth(handle_index) - .unwrap(); + .expect("Failed to get consumer builder at specified handle index"); - consumer_builder.build().unwrap() + consumer_builder.build().expect("Failed to build consumer instance") } // Create a producer for the specified service identifier @@ -160,8 +171,10 @@ fn create_producer( service_id: InstanceSpecifier, ) -> VehicleOfferedProducer { let producer_builder = runtime.producer_builder::(service_id); - let producer = producer_builder.build().unwrap(); - producer.offer().unwrap() + let producer = producer_builder + .build() + .expect("Failed to build producer instance"); + producer.offer().expect("Failed to offer producer instance") } // Run the example with the specified runtime @@ -176,12 +189,22 @@ fn run_with_runtime(name: &str, runtime: &R) { let tire_pressure = 5.0; println!("Setting tire pressure to {tire_pressure}"); for i in 0..5 { - monitor - .write_tire_data(Tire { - pressure: tire_pressure + i as f32, - }) - .unwrap(); - let tire_data = monitor.read_tire_data().unwrap(); + match monitor.write_tire_data(Tire { + pressure: tire_pressure + i as f32, + }) { + Ok(_) => (), + Err(e) => { + eprintln!("Failed to write tire data: {:?}", e); + continue; + } + } + let tire_data = match monitor.read_tire_data() { + Ok(data) => data, + Err(e) => { + eprintln!("Failed to read tire data: {:?}", e); + continue; + } + }; println!("{tire_data}"); } //unoffer returns producer back, so if needed it can be used further @@ -338,15 +361,21 @@ mod test { offered_producer: VehicleOfferedProducer, ) -> VehicleOfferedProducer { for i in 0..10 { - let uninit_sample = offered_producer.left_tire.allocate().unwrap(); + let uninit_sample = match offered_producer.left_tire.allocate() { + Ok(sample) => sample, + Err(e) => { + eprintln!("Failed to allocate sample: {:?}", e); + continue; + } + }; let sample = uninit_sample.write(Tire { pressure: 1.0 + i as f32, }); - sample.send().unwrap(); - println!( - "[SENDER] Sent sample with pressure: {:.2} psi", - 1.0 + i as f32 - ); + match sample.send() { + Ok(_) => (), + Err(e) => eprintln!("Failed to send sample: {:?}", e), + } + println!("Sent sample with pressure: {}", 1.0 + i as f32); tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; } offered_producer diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD index dab726eb3..76f9b0d18 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/BUILD +++ b/score/mw/com/impl/rust/com-api/com-api-concept/BUILD @@ -17,6 +17,7 @@ rust_library( name = "com-api-concept", srcs = [ "com_api_concept.rs", + "error.rs", "interface_macros.rs", "lib.rs", "reloc.rs", @@ -30,6 +31,7 @@ rust_library( ], deps = [ "@score_baselibs_rust//src/containers", + "@score_communication_crate_index//:thiserror", ], ) diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs index e7a05ecc2..08db9a69b 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/com_api_concept.rs @@ -48,6 +48,7 @@ //! - Structures //! - Tuples +use crate::error::*; use crate::Reloc; pub use com_api_concept_macros::CommData; use containers::fixed_capacity::FixedCapacityQueue; @@ -56,16 +57,6 @@ use core::future::Future; use core::ops::{Deref, DerefMut}; use std::path::Path; -/// Error enumeration for different failure cases in the Consumer/Producer/Runtime APIs. -#[derive(Debug)] -pub enum Error { - /// TODO: To be replaced, dummy value for "something went wrong" - Fail, - Timeout, - AllocateFailed, - SubscribeFailed, -} - /// Result type alias with `std::result::Result` using `com_api::Error` as error type pub type Result = core::result::Result; @@ -270,7 +261,7 @@ impl InstanceSpecifier { /// an `Error` on failure. /// /// # Errors - /// Returns `Error::Fail` if the provided string does not conform to the required format. + /// Returns `Error::InstanceSpecifierInvalid` if the provided string does not conform to the required format. pub fn new(service_name: impl AsRef) -> Result { let service_name = service_name.as_ref(); if Self::check_str(service_name) { @@ -278,7 +269,7 @@ impl InstanceSpecifier { specifier: service_name.to_string(), }) } else { - Err(Error::Fail) + Err(Error::ServiceError(ServiceFailedReason::InstanceSpecifierInvalid)) } } } @@ -712,11 +703,11 @@ impl SampleContainer { /// A `Result` indicating success or failure. /// /// # Errors - /// Returns 'Error' if container is already full. + /// Returns 'Error::AllocateError' if container is already full. pub fn push_back(&mut self, new: S) -> Result<()> { - self.inner - .push_back(new) - .map_err(|_| Error::AllocateFailed)?; + self.inner.push_back(new).map_err(|_| { + Error::AllocateError(AllocationFailureReason::OutOfMemory) + })?; Ok(()) } diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/error.rs b/score/mw/com/impl/rust/com-api/com-api-concept/error.rs new file mode 100644 index 000000000..ef7c2fd93 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-concept/error.rs @@ -0,0 +1,106 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +use thiserror::Error; + +/// Detailed information about Service Discovery failure reasons, +/// including specific issues with service interfaces, instance specifiers, and handle retrieval. +#[derive(Debug, Error)] +pub enum ServiceFailedReason { + #[error("Invalid instance specifier format or content, which may not be according to the expected format or contain invalid content")] + InstanceSpecifierInvalid, + #[error("Service not found, which may not be available currently or accessible")] + ServiceNotFound, + #[error("Failed to offer service instance for discovery")] + OfferServiceFailed, + #[error("Failed to start asynchronous discovery")] + FailedToStartDiscovery, +} + +/// Reason for producer failure +#[derive(Debug, Error)] +pub enum ProducerFailedReason { + #[error("Invalid instance specifier format or content, which may not be according to the expected format or contain invalid content")] + InstanceSpecifierInvalid, + #[error("Skeleton creation failed")] + SkeletonCreationFailed, + #[error("Builder creation failed for producer instance")] + BuilderCreationFailed, +} + +/// Reason for consumer failure +#[derive(Debug, Error)] +pub enum ConsumerFailedReason { + #[error("Invalid instance specifier format or content, which may not be according to the expected format or contain invalid content")] + InstanceSpecifierInvalid, + #[error("Service not found from service discovery handle")] + ServiceHandleNotFound, + #[error("Proxy creation failed")] + ProxyCreationFailed, +} + +/// Memory allocation error details +#[derive(Debug, Error)] +pub enum AllocationFailureReason { + #[error( + "Requested size exceeds available memory which is configured during subscription setup" + )] + OutOfMemory, + #[error("Invalid allocation request")] + InvalidRequest, + #[error("Failed to allocate sample in shared memory for skeleton event")] + AllocationToSharedMemoryFailed, +} + +/// Comprehensive error reasons for receive failure +#[derive(Debug, Error)] +pub enum ReceiveFailedReason { + #[error("Error at the time of initializing receive operation")] + InitializationFailed, + #[error("Receive operation failed because of internal error")] + ReceiveError, + #[error("Internal receive buffer not available for receive operation")] + BufferUnavailable, + #[error("Sample size out of bounds, expected at most {max}, but got {requested}")] + SampleCountOutOfBounds { max: usize, requested: usize }, +} + +/// Comprehensive error reasons for event-related failures +#[derive(Debug, Error)] +pub enum EventFailedReason { + #[error("Event creation error, possibly due to invalid event type or internal error")] + EventCreationFailed, + #[error("Event publication failed, possibly due to internal error")] + EventPublishFailed, + #[error("Failed to send sample data to the service instance, possibly due to internal error")] + SendingDataFailed, + #[error("Event not available for subscription, possibly due to missing event type or incompatible service")] + EventNotAvailable, +} + +/// Error enumeration for different failure cases in the Consumer/Producer/Runtime APIs. +#[derive(Debug, Error)] +pub enum Error { + #[error("Service error due to: {0}")] + ServiceError(ServiceFailedReason), + #[error("Producer error due to: {0}")] + ProducerError(ProducerFailedReason), + #[error("Consumer error due to: {0}")] + ConsumerError(ConsumerFailedReason), + #[error("Event operation failed due to: {0}")] + EventError(EventFailedReason), + #[error("Sample container allocation failed due to: {0}")] + AllocateError(AllocationFailureReason), + #[error("Receive operation failed due to: {0}")] + ReceiveError(ReceiveFailedReason), +} diff --git a/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs index 3b54b8644..a944aa17f 100644 --- a/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs +++ b/score/mw/com/impl/rust/com-api/com-api-concept/lib.rs @@ -20,9 +20,11 @@ /// while the Reloc type provides a safe abstraction for moving data across thread or process /// boundaries without violating Rust's ownership rules. mod com_api_concept; +mod error; mod interface_macros; mod reloc; pub use com_api_concept::*; +pub use error::*; #[doc(hidden)] pub use paste; pub use reloc::Reloc; diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/consumer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/consumer.rs index 04aadb41c..6bf807ecf 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/consumer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/consumer.rs @@ -34,6 +34,7 @@ use core::future::Future; use core::marker::PhantomData; use core::mem::ManuallyDrop; use core::ops::{Deref, DerefMut}; +use core::panic; use futures::task::{AtomicWaker, Context, Poll}; use std::cmp::Ordering; use std::pin::Pin; @@ -41,8 +42,9 @@ use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::Arc; use com_api_concept::{ - Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, Error, InstanceSpecifier, - Interface, Result, SampleContainer, ServiceDiscovery, Subscriber, Subscription, + Builder, CommData, Consumer, ConsumerBuilder, ConsumerDescriptor, ConsumerFailedReason, Error, + EventFailedReason, InstanceSpecifier, Interface, ReceiveFailedReason, Result, SampleContainer, + ServiceDiscovery, ServiceFailedReason, Subscriber, Subscription, }; use bridge_ffi_rs::*; @@ -223,11 +225,16 @@ impl std::fmt::Debug for NativeProxyBase { } impl NativeProxyBase { - pub fn new(interface_id: &str, handle: &HandleType) -> Self { + pub fn new(interface_id: &str, handle: &HandleType) -> Result { //SAFETY: It is safe to create the proxy because interface_id and handle are valid //Handle received at the time of get_avaible_instances called with correct interface_id let proxy = unsafe { bridge_ffi_rs::create_proxy(interface_id, handle) }; - Self { proxy } + if proxy.is_null() { + return Err(Error::ConsumerError( + ConsumerFailedReason::ProxyCreationFailed, + )); + } + Ok(Self { proxy }) } } @@ -249,12 +256,15 @@ pub struct NativeProxyEventBase { unsafe impl Send for NativeProxyEventBase {} impl NativeProxyEventBase { - pub fn new(proxy: *mut ProxyBase, interface_id: &str, identifier: &str) -> Self { + pub fn new(proxy: *mut ProxyBase, interface_id: &str, identifier: &str) -> Result { //SAFETY: It is safe as we are passing valid proxy pointer and interface id to get event // proxy pointer is created during consumer creation let proxy_event_ptr = unsafe { bridge_ffi_rs::get_event_from_proxy(proxy, interface_id, identifier) }; - Self { proxy_event_ptr } + if proxy_event_ptr.is_null() { + return Err(Error::EventError(EventFailedReason::EventCreationFailed)); + } + Ok(Self { proxy_event_ptr }) } /// Provides access to the underlying proxy event base. @@ -286,8 +296,10 @@ pub struct SubscribableImpl { impl Subscriber for SubscribableImpl { type Subscription = SubscriberImpl; fn new(identifier: &'static str, instance_info: LolaConsumerInfo) -> Result { - let handle = instance_info.get_handle().ok_or(Error::Fail)?; - let native_proxy = NativeProxyBase::new(instance_info.interface_id, handle); + let handle = instance_info.get_handle().ok_or(Error::ConsumerError( + ConsumerFailedReason::ServiceHandleNotFound, + ))?; + let native_proxy = NativeProxyBase::new(instance_info.interface_id, handle)?; let proxy_instance = ProxyInstanceManager(Arc::new(native_proxy)); Ok(Self { identifier, @@ -302,8 +314,7 @@ impl Subscriber for SubscribableImpl self.proxy_instance.0.proxy, self.instance_info.interface_id, self.identifier, - ); - + )?; //SAFETY: It is safe to subscribe to event because event_instance is valid // which was obtained from valid proxy instance let status = unsafe { @@ -313,7 +324,7 @@ impl Subscriber for SubscribableImpl ) }; if !status { - return Err(Error::Fail); + return Err(Error::EventError(EventFailedReason::EventNotAvailable)); } // Store in SubscriberImpl with event, max_num_samples Ok(SubscriberImpl { @@ -511,7 +522,12 @@ where ) -> impl Future>>> + 'a { async move { if max_samples > self.max_num_samples || new_samples > self.max_num_samples { - return Err(Error::Fail); + return Err(Error::ReceiveError( + ReceiveFailedReason::SampleCountOutOfBounds { + max: self.max_num_samples, + requested: max_samples.max(new_samples), + }, + )); } // Get the event guard to ensure no concurrent receive calls // on the same subscriber instance. @@ -522,7 +538,9 @@ where .load(std::sync::atomic::Ordering::Relaxed) { if let Err(_e) = self.init_async_receive(&mut event_guard) { - return Err(Error::Fail); + return Err(Error::ReceiveError( + ReceiveFailedReason::InitializationFailed, + )); } self.async_init_status .store(true, std::sync::atomic::Ordering::Relaxed); @@ -582,10 +600,10 @@ impl<'a, T: CommData + Debug> Future for ReceiveFuture<'a, T> { self.scratch = Some(scratch); result } else { - Err(Error::Fail) + Err(Error::ReceiveError(ReceiveFailedReason::ReceiveError)) } } else { - Err(Error::Fail) + Err(Error::ReceiveError(ReceiveFailedReason::BufferUnavailable)) } }; match samples_received { @@ -597,7 +615,10 @@ impl<'a, T: CommData + Debug> Future for ReceiveFuture<'a, T> { //event_guard will be dropped here, allowing new receive calls to access the // proxy event self.event_guard = None; - return Poll::Ready(self.scratch.take().ok_or(Error::Fail)); + return Poll::Ready(Ok(self + .scratch + .take() + .expect("SampleContainer is not available when returning Future result"))); } // Have some samples but not enough yet, wait for more via waker Poll::Pending @@ -658,12 +679,13 @@ where fn get_available_instances(&self) -> Result { //If ANY Support is added in Lola, then we need to return all available instances + //Once FFI layer error handling is in place (SWP-253124), we should convert this error to a proper FFI error instead of using map_err here let instance_specifier_lola = mw_com::InstanceSpecifier::try_from(self.instance_specifier.as_ref()) - .map_err(|_| Error::Fail)?; + .map_err(|_| Error::ServiceError(ServiceFailedReason::InstanceSpecifierInvalid))?; - let service_handle = - mw_com::proxy::find_service(instance_specifier_lola).map_err(|_| Error::Fail)?; + let service_handle = mw_com::proxy::find_service(instance_specifier_lola) + .map_err(|_| Error::ServiceError(ServiceFailedReason::ServiceNotFound))?; let service_handle_arc = Arc::new(service_handle); let available_instances = (0..service_handle_arc.len()) @@ -696,9 +718,10 @@ where let instance_specifier = self.instance_specifier.clone(); // Convert to Lola InstanceSpecifier early + //Once FFI layer error handling is in place (SWP-253124), we should convert this error to a proper FFI error instead of using map_err here let instance_specifier_lola = mw_com::InstanceSpecifier::try_from(instance_specifier.as_ref()) - .map_err(|_| Error::Fail); + .map_err(|_| Error::ServiceError(ServiceFailedReason::InstanceSpecifierInvalid)); let waker_storage = Arc::new(futures::task::AtomicWaker::new()); @@ -740,7 +763,9 @@ where // stop_find_service is always called in Drop. let raw_handle = unsafe { bridge_ffi_rs::start_find_service(&fat_ptr, spec) }; if raw_handle.is_null() { - Err(Error::Fail) + Err(Error::ServiceError( + ServiceFailedReason::FailedToStartDiscovery, + )) } else { // Single authoritative source of find_handle — return value only. // Callback's find_handle argument is ignored to prevent double-write. @@ -856,7 +881,10 @@ impl ConsumerDescriptor for SampleConsumerBuilder fn get_instance_identifier(&self) -> &InstanceSpecifier { //if InstanceSpecifier::ANY support enable by lola //then this API should get InstanceSpecifier from FFI Call - todo!() + panic!( + "InstanceSpecifier::ANY is not supported in LolaRuntimeImpl, + Please use FindServiceSpecifier::Specific with a valid instance specifier." + ); } } @@ -877,7 +905,12 @@ fn try_receive_samples( max_samples: usize, ) -> Result { if max_samples > max_num_samples { - return Err(Error::Fail); + return Err(Error::ReceiveError( + ReceiveFailedReason::SampleCountOutOfBounds { + max: max_num_samples, + requested: max_samples, + }, + )); } // Create a callback that will be called by the C++ side for each new sample arrival let mut callback = create_sample_callback(scratch, max_samples); @@ -897,7 +930,12 @@ fn try_receive_samples( ) }; if count > max_num_samples as u32 { - return Err(Error::Fail); + return Err(Error::ReceiveError( + ReceiveFailedReason::SampleCountOutOfBounds { + max: max_num_samples, + requested: count as usize, + }, + )); } Ok(count as usize) } diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs index 784e3cf60..13c93f5cb 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/producer.rs @@ -36,8 +36,9 @@ use core::ops::{Deref, DerefMut}; use std::sync::Arc; use com_api_concept::{ - Builder, CommData, Error, InstanceSpecifier, Interface, Producer, ProducerBuilder, - ProviderInfo, Result, + AllocationFailureReason, Builder, CommData, Error, EventFailedReason, InstanceSpecifier, + Interface, Producer, ProducerBuilder, ProducerFailedReason, ProviderInfo, Result, + ServiceFailedReason, }; use bridge_ffi_rs::*; @@ -58,7 +59,7 @@ impl ProviderInfo for LolaProviderInfo { let status = unsafe { bridge_ffi_rs::skeleton_offer_service(self.skeleton_handle.0.handle) }; if !status { - return Err(Error::Fail); + return Err(Error::ServiceError(ServiceFailedReason::OfferServiceFailed)); } Ok(()) } @@ -187,7 +188,7 @@ where ) }; if !status { - return Err(Error::Fail); + return Err(Error::EventError(EventFailedReason::SendingDataFailed)); } Ok(()) } @@ -297,11 +298,16 @@ unsafe impl Sync for NativeSkeletonHandle {} unsafe impl Send for NativeSkeletonHandle {} impl NativeSkeletonHandle { - pub fn new(interface_id: &str, instance_specifier: &mw_com::InstanceSpecifier) -> Self { + pub fn new(interface_id: &str, instance_specifier: &mw_com::InstanceSpecifier) -> Result { //SAFETY: It is safe as we are passing valid type id and instance specifier to create skeleton let handle = unsafe { bridge_ffi_rs::create_skeleton(interface_id, instance_specifier.as_native()) }; - Self { handle } + if handle.is_null() { + return Err(Error::ProducerError( + ProducerFailedReason::SkeletonCreationFailed, + )); + } + Ok(Self { handle }) } } @@ -329,7 +335,7 @@ pub struct NativeSkeletonEventBase { unsafe impl Send for NativeSkeletonEventBase {} impl NativeSkeletonEventBase { - pub fn new(instance_info: &LolaProviderInfo, identifier: &str) -> Self { + pub fn new(instance_info: &LolaProviderInfo, identifier: &str) -> Result { //SAFETY: It is safe as we are passing valid skeleton handle and interface id to get event // skeleton handle is created during producer offer call let skeleton_event_ptr = unsafe { @@ -339,7 +345,12 @@ impl NativeSkeletonEventBase { identifier, ) }; - Self { skeleton_event_ptr } + if skeleton_event_ptr.is_null() { + return Err(Error::ProducerError( + ProducerFailedReason::SkeletonCreationFailed, + )); + } + Ok(Self { skeleton_event_ptr }) } } @@ -389,7 +400,9 @@ where T::ID, ); if !status { - return Err(Error::Fail); + return Err(Error::AllocateError( + AllocationFailureReason::AllocationToSharedMemoryFailed, + )); } sample.assume_init() }; @@ -404,8 +417,7 @@ where } fn new(identifier: &str, instance_info: LolaProviderInfo) -> Result { - let skeleton_event = NativeSkeletonEventBase::new(&instance_info, identifier); - + let skeleton_event = NativeSkeletonEventBase::new(&instance_info, identifier)?; Ok(Self { identifier: identifier.to_string(), skeleton_event, @@ -433,20 +445,21 @@ impl ProducerBuilder for SampleProducerBuilder impl Builder> for SampleProducerBuilder { fn build(self) -> Result> { - let instance_specifier_runtime = - mw_com::InstanceSpecifier::try_from(self.instance_specifier.as_ref()) - .map_err(|_| Error::Fail)?; + //Once FFI layer error handling is in place (SWP-253124), we should convert this error to a proper FFI error instead of using map_err here + let instance_specifier_runtime = mw_com::InstanceSpecifier::try_from( + self.instance_specifier.as_ref(), + ) + .map_err(|_| Error::ProducerError(ProducerFailedReason::InstanceSpecifierInvalid))?; let skeleton_handle = - NativeSkeletonHandle::new(I::INTERFACE_ID, &instance_specifier_runtime); - + NativeSkeletonHandle::new(I::INTERFACE_ID, &instance_specifier_runtime)?; let instance_info = LolaProviderInfo { instance_specifier: self.instance_specifier, interface_id: I::INTERFACE_ID, skeleton_handle: SkeletonInstanceManager(Arc::new(skeleton_handle)), }; - I::Producer::new(instance_info).map_err(|_| Error::Fail) + I::Producer::new(instance_info) } } diff --git a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs index 529def246..927051feb 100644 --- a/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs +++ b/score/mw/com/impl/rust/com-api/com-api-runtime-lola/runtime.rs @@ -39,7 +39,10 @@ impl Runtime for LolaRuntimeImpl { ) -> Self::ServiceDiscovery { SampleConsumerDiscovery { instance_specifier: match instance_specifier { - FindServiceSpecifier::Any => todo!(), // TODO:[eclipse-score/communication/issues/133]Add error msg or panic like "ANY not supported by Lola" + FindServiceSpecifier::Any => panic!( + "FindServiceSpecifier::Any is not supported in LolaRuntimeImpl, + Please use FindServiceSpecifier::Specific with a valid instance specifier." + ), FindServiceSpecifier::Specific(spec) => spec, }, _interface: PhantomData,