use alloc::sync::Arc;
use core::{any::TypeId, fmt::Debug, marker::PhantomData, mem::MaybeUninit, ops::Deref};
use flatbuffers::InvalidFlatbuffer;
use iceoryx2_bb_elementary::allocation_strategy::AllocationStrategy;
use iceoryx2_bb_elementary_traits::iceoryx_send::IceoryxSend;
use iceoryx2_bb_elementary_traits::testing::abandonable::Abandonable;
use iceoryx2_bb_flatbuffers::FlatbufferError;
use iceoryx2_cal::zero_copy_connection::ChannelState;
use iceoryx2_bb_concurrency::atomic::AtomicUsize;
use iceoryx2_bb_concurrency::atomic::Ordering;
use iceoryx2_bb_elementary_traits::zero_copy_send::ZeroCopySend;
use iceoryx2_cal::{arc_sync_policy::ArcSyncPolicy, zero_copy_connection::ChannelId};
use iceoryx2_log::fail;
use crate::payload::number_of_elements;
use crate::port::details::chunk::Chunk;
use crate::port::details::chunk::ChunkMut;
use crate::service::marker::CustomHeaderMarker;
use crate::service::marker::CustomPayloadMarker;
use crate::service::marker::Flatbuffer;
use crate::unique_id_generator::UniqueId;
use crate::{
identifiers::{UniqueClientId, UniqueServerId},
port::{
LoanError, SendError,
details::chunk_details::ChunkDetails,
server::{INVALID_CONNECTION_ID, SharedServerState},
},
response_mut::ResponseMut,
response_mut_uninit::ResponseMutUninit,
service::{self, static_config::message_type_details::TypeVariant},
};
pub type RequestId = ChannelState;
pub struct ActiveRequest<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> {
pub(crate) chunk: Chunk,
pub(crate) shared_state: Service::ArcThreadSafetyPolicy<SharedServerState<Service>>,
pub(crate) shared_loan_counter: Arc<AtomicUsize>,
pub(crate) max_loan_count: usize,
pub(crate) details: ChunkDetails,
pub(crate) request_id: RequestId,
pub(crate) channel_id: ChannelId,
pub(crate) connection_id: usize,
pub(crate) _request_payload: PhantomData<RequestPayload>,
pub(crate) _request_header: PhantomData<RequestHeader>,
pub(crate) _response_payload: PhantomData<ResponsePayload>,
pub(crate) _response_header: PhantomData<ResponseHeader>,
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> Abandonable
for ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
{
unsafe fn abandon_in_place(mut this: core::ptr::NonNull<Self>) {
let this = unsafe { this.as_mut() };
unsafe { core::ptr::drop_in_place(&mut this.shared_state) };
unsafe { core::ptr::drop_in_place(&mut this.shared_loan_counter) };
}
}
unsafe impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> Send for ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
where
Service::ArcThreadSafetyPolicy<SharedServerState<Service>>: Send + Sync,
{
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> Debug for ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"ActiveRequest<{}, {}, {}, {}, {}> {{ details: {:?}, request_id: {}, channel_id: {} }}",
core::any::type_name::<Service>(),
core::any::type_name::<RequestPayload>(),
core::any::type_name::<RequestHeader>(),
core::any::type_name::<ResponsePayload>(),
core::any::type_name::<ResponseHeader>(),
self.details,
self.request_id.value(),
self.channel_id.value()
)
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ZeroCopySend,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> Deref for ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
{
type Target = RequestPayload;
fn deref(&self) -> &Self::Target {
unsafe { &*self.chunk.payload_ptr().cast() }
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ZeroCopySend,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> Deref
for ActiveRequest<Service, [RequestPayload], RequestHeader, ResponsePayload, ResponseHeader>
{
type Target = [RequestPayload];
fn deref(&self) -> &Self::Target {
let payload_size = self.shared_state.lock().request_receiver.payload_size();
unsafe {
&*core::ptr::slice_from_raw_parts(
self.chunk.payload_ptr().cast(),
number_of_elements::<RequestPayload, _>(self.header(), payload_size),
)
}
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> Drop for ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
{
fn drop(&mut self) {
self.shared_state
.lock()
.request_receiver
.release_offset(&self.details, ChannelId::new(0));
self.finish();
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ZeroCopySend,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
{
pub fn payload(&self) -> &RequestPayload {
self.deref()
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ZeroCopySend,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> ActiveRequest<Service, [RequestPayload], RequestHeader, ResponsePayload, ResponseHeader>
{
pub fn payload(&self) -> &[RequestPayload] {
self.deref()
}
}
impl<
Service: crate::service::Service,
RequestPayload,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
>
ActiveRequest<
Service,
Flatbuffer<RequestPayload>,
RequestHeader,
ResponsePayload,
ResponseHeader,
>
{
pub fn payload_bytes(&self) -> &[u8] {
let payload_offset = self.header().payload_offset as usize;
let payload_ptr = self.chunk.payload_ptr();
let payload_len = self.header().number_of_elements as usize;
unsafe {
core::slice::from_raw_parts(
payload_ptr.add(payload_offset),
payload_len - payload_offset,
)
}
}
pub fn payload_root<'a>(
&'a self,
) -> Result<RequestPayload::Inner, FlatbufferError<InvalidFlatbuffer>>
where
RequestPayload: flatbuffers::Follow<'a> + flatbuffers::Verifiable,
{
Ok(flatbuffers::root::<RequestPayload>(self.payload_bytes())?)
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + IceoryxSend + ?Sized,
ResponseHeader: Debug + ZeroCopySend,
> ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
{
fn finish(&self) {
if self.connection_id != INVALID_CONNECTION_ID {
self.shared_state.lock().response_sender.close_channel(
self.channel_id,
self.connection_id,
self.request_id,
);
}
}
pub fn has_disconnect_hint(&self) -> bool {
if self.connection_id != INVALID_CONNECTION_ID {
self.shared_state
.lock()
.response_sender
.has_disconnect_hint(self.channel_id, self.connection_id, self.request_id)
} else {
false
}
}
pub fn is_connected(&self) -> bool {
if self.connection_id != INVALID_CONNECTION_ID {
self.shared_state.lock().response_sender.has_channel_state(
self.channel_id,
self.connection_id,
self.request_id,
)
} else {
false
}
}
pub fn user_header(&self) -> &RequestHeader {
unsafe { &*self.chunk.user_header_ptr().cast() }
}
pub fn header(&self) -> &crate::service::header::request_response::RequestHeader {
unsafe { &*self.chunk.header_ptr().cast() }
}
pub fn origin(&self) -> UniqueClientId {
UniqueClientId(unsafe { UniqueId::from_raw_id(self.details.origin) })
}
fn increment_loan_counter(&self) -> Result<(), LoanError> {
let mut current_loan_count = self.shared_loan_counter.load(Ordering::Relaxed);
loop {
if self.max_loan_count <= current_loan_count {
fail!(from self,
with LoanError::ExceedsMaxLoans,
"Unable to loan memory for Response since it would exceed the maximum number of loans of {}.",
self.max_loan_count);
}
match self.shared_loan_counter.compare_exchange(
current_loan_count,
current_loan_count + 1,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => return Ok(()),
Err(v) => current_loan_count = v,
}
}
}
fn loan_chunk(&self, slice_len: usize) -> Result<ChunkMut, LoanError>
where
ResponseHeader: Default,
{
self.increment_loan_counter()?;
let shared_state = self.shared_state.lock();
let chunk = shared_state
.response_sender
.allocate(shared_state.response_sender.chunk_layout(slice_len))?;
let header_ptr: *mut service::header::request_response::ResponseHeader =
chunk.header.cast();
let user_header_ptr: *mut ResponseHeader = chunk.user_header.cast();
unsafe {
header_ptr.write(service::header::request_response::ResponseHeader {
node_id: *shared_state.response_sender.shared_node.id(),
server_id: UniqueServerId(UniqueId::from_raw_id(
shared_state.response_sender.sender_port_id,
)),
request_id: self.request_id,
number_of_elements: slice_len as _,
payload_offset: 0,
})
};
unsafe { user_header_ptr.write(ResponseHeader::default()) };
Ok(chunk)
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + ZeroCopySend + Sized,
ResponseHeader: Default + Debug + ZeroCopySend,
> ActiveRequest<Service, RequestPayload, RequestHeader, ResponsePayload, ResponseHeader>
{
pub fn loan_uninit(
&self,
) -> Result<ResponseMutUninit<Service, MaybeUninit<ResponsePayload>, ResponseHeader>, LoanError>
{
ResponseMutUninit::new(
&self.shared_state,
self.loan_chunk(1)?,
&self.shared_loan_counter,
self.channel_id,
self.connection_id,
)
}
pub fn send_copy(&self, value: ResponsePayload) -> Result<(), SendError> {
let msg = "Unable to send copy of response";
let response = fail!(from self,
when self.loan_uninit(),
"{} since the loan of the response failed.", msg);
response.write_payload(value).send()
}
pub fn loan(&self) -> Result<ResponseMut<Service, ResponsePayload, ResponseHeader>, LoanError>
where
ResponsePayload: Default,
{
Ok(self
.loan_uninit()?
.write_payload(ResponsePayload::default()))
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug + ZeroCopySend + 'static,
ResponseHeader: Default + Debug + ZeroCopySend,
> ActiveRequest<Service, RequestPayload, RequestHeader, [ResponsePayload], ResponseHeader>
{
pub fn loan_slice(
&self,
slice_len: usize,
) -> Result<ResponseMut<Service, [ResponsePayload], ResponseHeader>, LoanError>
where
ResponsePayload: Default,
{
let response = self.loan_slice_uninit(slice_len)?;
Ok(response.write_from_fn(|_| ResponsePayload::default()))
}
pub fn loan_slice_uninit(
&self,
slice_len: usize,
) -> Result<ResponseMutUninit<Service, [MaybeUninit<ResponsePayload>], ResponseHeader>, LoanError>
{
debug_assert!(TypeId::of::<ResponsePayload>() != TypeId::of::<CustomPayloadMarker>());
unsafe { self.loan_slice_uninit_impl(slice_len) }
}
unsafe fn loan_slice_uninit_impl(
&self,
slice_len: usize,
) -> Result<ResponseMutUninit<Service, [MaybeUninit<ResponsePayload>], ResponseHeader>, LoanError>
{
{
let shared_state = self.shared_state.lock();
let max_slice_len = shared_state.config.initial_max_slice_len;
if shared_state.config.allocation_strategy == AllocationStrategy::Static
&& max_slice_len < slice_len
{
fail!(from self, with LoanError::ExceedsMaxLoanSize,
"Unable to loan slice with {} elements since it would exceed the max supported slice length of {}.",
slice_len, max_slice_len);
}
}
ResponseMutUninit::new(
&self.shared_state,
self.loan_chunk(slice_len)?,
&self.shared_loan_counter,
self.channel_id,
self.connection_id,
)
}
}
impl<Service: crate::service::Service>
ActiveRequest<
Service,
[CustomPayloadMarker],
CustomHeaderMarker,
[CustomPayloadMarker],
CustomHeaderMarker,
>
{
#[doc(hidden)]
pub unsafe fn loan_custom_payload(
&self,
slice_len: usize,
) -> Result<
ResponseMutUninit<Service, [MaybeUninit<CustomPayloadMarker>], CustomHeaderMarker>,
LoanError,
> {
let shared_state = self.shared_state.lock();
debug_assert!(
slice_len == 1
|| shared_state.response_sender.payload_type_variant() == TypeVariant::Dynamic
);
unsafe { self.loan_slice_uninit_impl(slice_len) }
}
}
impl<
Service: crate::service::Service,
RequestPayload: Debug + IceoryxSend + ?Sized,
RequestHeader: Debug + ZeroCopySend,
ResponsePayload: Debug,
ResponseHeader: Default + Debug + ZeroCopySend,
>
ActiveRequest<
Service,
RequestPayload,
RequestHeader,
Flatbuffer<ResponsePayload>,
ResponseHeader,
>
{
pub fn loan_flatbuffer(
&self,
) -> Result<ResponseMutUninit<Service, Flatbuffer<ResponsePayload>, ResponseHeader>, LoanError>
{
ResponseMutUninit::new_flatbuffer(
&self.shared_state,
self.loan_chunk(1)?,
&self.shared_loan_counter,
self.channel_id,
self.connection_id,
)
}
}