zenoh_shm/api/protocol_implementations/posix/
posix_shm_provider_backend_talc.rs

1//
2// Copyright (c) 2025 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15use std::{
16    alloc::Layout,
17    ptr::NonNull,
18    slice,
19    sync::{Arc, Mutex},
20};
21
22use talc::{ErrOnOom, Talc};
23use zenoh_core::{zlock, Resolvable, Wait};
24use zenoh_result::ZResult;
25
26use super::posix_shm_segment::PosixShmSegment;
27use crate::api::{
28    common::{types::ProtocolID, with_id::WithProtocolID},
29    protocol_implementations::posix::protocol_id::POSIX_PROTOCOL_ID,
30    provider::{
31        chunk::ChunkDescriptor,
32        memory_layout::MemoryLayout,
33        shm_provider_backend::ShmProviderBackend,
34        types::{AllocAlignment, ChunkAllocResult, ZAllocError, ZLayoutError},
35    },
36};
37
38/// Builder to create posix SHM provider
39#[zenoh_macros::unstable_doc]
40pub struct PosixShmProviderBackendTalcBuilder<Layout> {
41    layout: Layout,
42}
43
44#[zenoh_macros::unstable_doc]
45impl<Layout> Resolvable for PosixShmProviderBackendTalcBuilder<Layout> {
46    type To = ZResult<PosixShmProviderBackendTalc>;
47}
48
49#[zenoh_macros::unstable_doc]
50impl<Layout: TryInto<MemoryLayout>> Wait for PosixShmProviderBackendTalcBuilder<Layout>
51where
52    Layout::Error: Into<ZLayoutError>,
53{
54    fn wait(self) -> <Self as Resolvable>::To {
55        PosixShmProviderBackendTalc::new(&self.layout.try_into().map_err(Into::into)?)
56    }
57}
58
59/// A talc backend based on POSIX shared memory.
60/// This is the default general-purpose backend shipped with Zenoh.
61/// Talc allocator provides great performnce (2nd after `buddy_system_allocator`) while maintaining
62/// excellent fragmentation resistance and memory utilization efficiency.
63#[zenoh_macros::unstable_doc]
64pub struct PosixShmProviderBackendTalc {
65    segment: Arc<PosixShmSegment>,
66    talc: Mutex<Talc<ErrOnOom>>,
67    alignment: AllocAlignment,
68}
69
70impl PosixShmProviderBackendTalc {
71    /// Get the builder to construct a new instance
72    #[zenoh_macros::unstable_doc]
73    pub fn builder<Layout>(layout: Layout) -> PosixShmProviderBackendTalcBuilder<Layout> {
74        PosixShmProviderBackendTalcBuilder { layout }
75    }
76
77    fn new(layout: &MemoryLayout) -> ZResult<Self> {
78        let segment = Arc::new(PosixShmSegment::create(layout.size())?);
79
80        // because of platform specific, our shm segment is >= requested size, so in order to utilize
81        // additional memory we re-layout the size
82        let real_size = segment.segment.elem_count().get();
83        let ptr = unsafe { segment.segment.elem_mut(0) };
84
85        let mut talc = Talc::new(ErrOnOom);
86
87        unsafe {
88            talc.claim(slice::from_raw_parts_mut(ptr, real_size).into())
89                .map_err(|_| "Error initializing Talc backend!")?;
90        }
91
92        tracing::trace!(
93            "Created PosixShmProviderBackendTalc id {}, layout {:?}",
94            segment.segment.id(),
95            layout
96        );
97
98        Ok(Self {
99            segment,
100            talc: Mutex::new(talc),
101            alignment: layout.alignment(),
102        })
103    }
104}
105
106impl WithProtocolID for PosixShmProviderBackendTalc {
107    fn id(&self) -> ProtocolID {
108        POSIX_PROTOCOL_ID
109    }
110}
111
112impl ShmProviderBackend for PosixShmProviderBackendTalc {
113    fn alloc(&self, layout: &MemoryLayout) -> ChunkAllocResult {
114        tracing::trace!("PosixShmProviderBackendTalc::alloc({:?})", layout);
115
116        let alloc_layout = unsafe {
117            Layout::from_size_align_unchecked(
118                layout.size().get(),
119                layout.alignment().get_alignment_value().get(),
120            )
121        };
122
123        let alloc = {
124            let mut lock = zlock!(self.talc);
125            unsafe { lock.malloc(alloc_layout) }
126        };
127
128        match alloc {
129            Ok(buf) => Ok(self.segment.clone().allocated_chunk(buf, layout)),
130            Err(_) => Err(ZAllocError::OutOfMemory),
131        }
132    }
133
134    fn free(&self, chunk: &ChunkDescriptor) {
135        let alloc_layout = unsafe {
136            Layout::from_size_align_unchecked(
137                chunk.len.get(),
138                self.alignment.get_alignment_value().get(),
139            )
140        };
141
142        let ptr = unsafe { self.segment.segment.elem_mut(chunk.chunk) };
143
144        unsafe { zlock!(self.talc).free(NonNull::new_unchecked(ptr), alloc_layout) };
145    }
146
147    fn defragment(&self) -> usize {
148        0
149    }
150
151    fn available(&self) -> usize {
152        0
153    }
154
155    fn layout_for(&self, layout: MemoryLayout) -> Result<MemoryLayout, ZLayoutError> {
156        layout.extend(self.alignment)
157    }
158}