From f6cdc54ca12eb4499c6836c7a60dbc062e13963f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 07:06:38 +0000 Subject: [PATCH 1/8] Initial plan From d9fd95f332813cd3708e139bc97b3a4fe0f37841 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 07:15:55 +0000 Subject: [PATCH 2/8] Add debug example to measure future sizes and confirm the issue Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- examples/debug_future_size.rs | 144 ++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 examples/debug_future_size.rs diff --git a/examples/debug_future_size.rs b/examples/debug_future_size.rs new file mode 100644 index 0000000..fd0d117 --- /dev/null +++ b/examples/debug_future_size.rs @@ -0,0 +1,144 @@ +//! Debug example to measure future sizes in the Matter stack +#![recursion_limit = "256"] + +use core::pin::pin; + +use embassy_futures::select::select; +use embassy_time::{Duration, Timer}; + +use env_logger::Target; +use log::info; + +use rs_matter_stack::eth::EthMatterStack; +use rs_matter_stack::matter::dm::clusters::desc; +use rs_matter_stack::matter::dm::clusters::desc::ClusterHandler as _; +use rs_matter_stack::matter::dm::clusters::on_off; +use rs_matter_stack::matter::dm::clusters::on_off::ClusterHandler as _; +use rs_matter_stack::matter::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET}; +use rs_matter_stack::matter::dm::devices::DEV_TYPE_ON_OFF_LIGHT; +use rs_matter_stack::matter::dm::networks::unix::UnixNetifs; +use rs_matter_stack::matter::dm::{Async, Dataver, Endpoint, Node}; +use rs_matter_stack::matter::dm::{EmptyHandler, EpClMatcher}; +use rs_matter_stack::matter::error::Error; +use rs_matter_stack::matter::utils::init::InitMaybeUninit; +use rs_matter_stack::matter::utils::select::Coalesce; +use rs_matter_stack::matter::{clusters, devices}; +use rs_matter_stack::mdns::ZeroconfMdns; +use rs_matter_stack::persist::DirKvBlobStore; + +use static_cell::StaticCell; + +fn main() -> Result<(), Error> { + env_logger::Builder::from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ) + .target(Target::Stdout) + .init(); + + info!("Starting debug example..."); + + // Initialize the Matter stack (can be done only once), + // as we'll run it in this thread + let stack = MATTER_STACK + .uninit() + .init_with(EthMatterStack::init_default( + &TEST_DEV_DET, + TEST_DEV_COMM, + &TEST_DEV_ATT, + )); + + // Our "light" on-off cluster. + // Can be anything implementing `rs_matter::dm::AsyncHandler` + let on_off = on_off::OnOffHandler::new(Dataver::new_rand(stack.matter().rand())); + + // Chain our endpoint clusters with the + // (root) Endpoint 0 system clusters in the final handler + let handler = EmptyHandler + .chain( + EpClMatcher::new( + Some(LIGHT_ENDPOINT_ID), + Some(on_off::OnOffHandler::CLUSTER.id), + ), + Async(on_off::HandlerAdaptor(&on_off)), + ) + // Each Endpoint needs a Descriptor cluster too + // Just use the one that `rs-matter` provides out of the box + .chain( + EpClMatcher::new(Some(LIGHT_ENDPOINT_ID), Some(desc::DescHandler::CLUSTER.id)), + Async(desc::DescHandler::new(Dataver::new_rand(stack.matter().rand())).adapt()), + ); + + let store = stack.create_shared_store(DirKvBlobStore::new_default()); + + // Create the future and measure its size before pinning + let matter_future = stack.run_preex( + // The Matter stack needs UDP sockets to communicate with other Matter devices + edge_nal_std::Stack::new(), + // Will try to find a default network interface + UnixNetifs, + // Will use the mDNS implementation based on the `zeroconf` crate + ZeroconfMdns, + // Will persist in `/rs-matter` + &store, + // Our `AsyncHandler` + `AsyncMetadata` impl + (NODE, handler), + // No user task future to run + (), + ); + + info!("Matter futures size: {}B", core::mem::size_of_val(&matter_future)); + + let mut matter = pin!(matter_future); + + // Just for demoing purposes: + // + // Run a sample loop that simulates state changes triggered by the HAL + // Changes will be properly communicated to the Matter controllers + // (i.e. Google Home, Alexa) and other Matter devices thanks to subscriptions + let device_future = async { + loop { + // Simulate user toggling the light with a physical switch every 5 seconds + Timer::after(Duration::from_secs(5)).await; + + // Toggle + on_off.set(!on_off.get()); + + // Let the Matter stack know that we have changed + // the state of our Light device + stack.notify_changed(); + + info!("Light toggled"); + } + }; + + info!("Device futures size: {}B", core::mem::size_of_val(&device_future)); + + let mut device = pin!(device_future); + + // Schedule the Matter run & the device loop together + let combined_future = select(&mut matter, &mut device).coalesce(); + info!("Combined futures size: {}B", core::mem::size_of_val(&combined_future)); + + futures_lite::future::block_on(combined_future) +} + +/// The Matter stack is allocated statically to avoid +/// program stack blowups. +static MATTER_STACK: StaticCell = StaticCell::new(); + +/// Endpoint 0 (the root endpoint) always runs +/// the hidden Matter system clusters, so we pick ID=1 +const LIGHT_ENDPOINT_ID: u16 = 1; + +/// The Matter Light device Node +const NODE: Node = Node { + id: 0, + endpoints: &[ + EthMatterStack::<()>::root_endpoint(), + Endpoint { + id: LIGHT_ENDPOINT_ID, + device_types: devices!(DEV_TYPE_ON_OFF_LIGHT), + clusters: clusters!(desc::DescHandler::CLUSTER, on_off::OnOffHandler::CLUSTER), + }, + ], +}; \ No newline at end of file From 4fec86e60109f1954f124e578869158c68831d1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 07:22:55 +0000 Subject: [PATCH 3/8] Optimize async future memory usage by boxing large futures Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- examples/debug_future_size.rs | 144 ---------------------------------- src/eth.rs | 9 ++- src/wireless/thread.rs | 26 +++--- src/wireless/wifi.rs | 11 ++- 4 files changed, 28 insertions(+), 162 deletions(-) delete mode 100644 examples/debug_future_size.rs diff --git a/examples/debug_future_size.rs b/examples/debug_future_size.rs deleted file mode 100644 index fd0d117..0000000 --- a/examples/debug_future_size.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Debug example to measure future sizes in the Matter stack -#![recursion_limit = "256"] - -use core::pin::pin; - -use embassy_futures::select::select; -use embassy_time::{Duration, Timer}; - -use env_logger::Target; -use log::info; - -use rs_matter_stack::eth::EthMatterStack; -use rs_matter_stack::matter::dm::clusters::desc; -use rs_matter_stack::matter::dm::clusters::desc::ClusterHandler as _; -use rs_matter_stack::matter::dm::clusters::on_off; -use rs_matter_stack::matter::dm::clusters::on_off::ClusterHandler as _; -use rs_matter_stack::matter::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET}; -use rs_matter_stack::matter::dm::devices::DEV_TYPE_ON_OFF_LIGHT; -use rs_matter_stack::matter::dm::networks::unix::UnixNetifs; -use rs_matter_stack::matter::dm::{Async, Dataver, Endpoint, Node}; -use rs_matter_stack::matter::dm::{EmptyHandler, EpClMatcher}; -use rs_matter_stack::matter::error::Error; -use rs_matter_stack::matter::utils::init::InitMaybeUninit; -use rs_matter_stack::matter::utils::select::Coalesce; -use rs_matter_stack::matter::{clusters, devices}; -use rs_matter_stack::mdns::ZeroconfMdns; -use rs_matter_stack::persist::DirKvBlobStore; - -use static_cell::StaticCell; - -fn main() -> Result<(), Error> { - env_logger::Builder::from_env( - env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), - ) - .target(Target::Stdout) - .init(); - - info!("Starting debug example..."); - - // Initialize the Matter stack (can be done only once), - // as we'll run it in this thread - let stack = MATTER_STACK - .uninit() - .init_with(EthMatterStack::init_default( - &TEST_DEV_DET, - TEST_DEV_COMM, - &TEST_DEV_ATT, - )); - - // Our "light" on-off cluster. - // Can be anything implementing `rs_matter::dm::AsyncHandler` - let on_off = on_off::OnOffHandler::new(Dataver::new_rand(stack.matter().rand())); - - // Chain our endpoint clusters with the - // (root) Endpoint 0 system clusters in the final handler - let handler = EmptyHandler - .chain( - EpClMatcher::new( - Some(LIGHT_ENDPOINT_ID), - Some(on_off::OnOffHandler::CLUSTER.id), - ), - Async(on_off::HandlerAdaptor(&on_off)), - ) - // Each Endpoint needs a Descriptor cluster too - // Just use the one that `rs-matter` provides out of the box - .chain( - EpClMatcher::new(Some(LIGHT_ENDPOINT_ID), Some(desc::DescHandler::CLUSTER.id)), - Async(desc::DescHandler::new(Dataver::new_rand(stack.matter().rand())).adapt()), - ); - - let store = stack.create_shared_store(DirKvBlobStore::new_default()); - - // Create the future and measure its size before pinning - let matter_future = stack.run_preex( - // The Matter stack needs UDP sockets to communicate with other Matter devices - edge_nal_std::Stack::new(), - // Will try to find a default network interface - UnixNetifs, - // Will use the mDNS implementation based on the `zeroconf` crate - ZeroconfMdns, - // Will persist in `/rs-matter` - &store, - // Our `AsyncHandler` + `AsyncMetadata` impl - (NODE, handler), - // No user task future to run - (), - ); - - info!("Matter futures size: {}B", core::mem::size_of_val(&matter_future)); - - let mut matter = pin!(matter_future); - - // Just for demoing purposes: - // - // Run a sample loop that simulates state changes triggered by the HAL - // Changes will be properly communicated to the Matter controllers - // (i.e. Google Home, Alexa) and other Matter devices thanks to subscriptions - let device_future = async { - loop { - // Simulate user toggling the light with a physical switch every 5 seconds - Timer::after(Duration::from_secs(5)).await; - - // Toggle - on_off.set(!on_off.get()); - - // Let the Matter stack know that we have changed - // the state of our Light device - stack.notify_changed(); - - info!("Light toggled"); - } - }; - - info!("Device futures size: {}B", core::mem::size_of_val(&device_future)); - - let mut device = pin!(device_future); - - // Schedule the Matter run & the device loop together - let combined_future = select(&mut matter, &mut device).coalesce(); - info!("Combined futures size: {}B", core::mem::size_of_val(&combined_future)); - - futures_lite::future::block_on(combined_future) -} - -/// The Matter stack is allocated statically to avoid -/// program stack blowups. -static MATTER_STACK: StaticCell = StaticCell::new(); - -/// Endpoint 0 (the root endpoint) always runs -/// the hidden Matter system clusters, so we pick ID=1 -const LIGHT_ENDPOINT_ID: u16 = 1; - -/// The Matter Light device Node -const NODE: Node = Node { - id: 0, - endpoints: &[ - EthMatterStack::<()>::root_endpoint(), - Endpoint { - id: LIGHT_ENDPOINT_ID, - device_types: devices!(DEV_TYPE_ON_OFF_LIGHT), - clusters: clusters!(desc::DescHandler::CLUSTER, on_off::OnOffHandler::CLUSTER), - }, - ], -}; \ No newline at end of file diff --git a/src/eth.rs b/src/eth.rs index 5322e1e..e435140 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -1,4 +1,6 @@ use core::pin::pin; +extern crate alloc; +use alloc::boxed::Box; use embassy_futures::select::{select, select3}; @@ -277,7 +279,8 @@ where { info!("Ethernet driver started"); - let mut net_task = pin!(self.0.run_oper_net( + // Box the largest futures to reduce stack frame size + let net_task = Box::pin(self.0.run_oper_net( &net_stack, &netif, &mut mdns, @@ -286,11 +289,11 @@ where )); let handler = self.0.root_handler(&(), &true, &netif, &self.1); - let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); + let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); - select3(&mut net_task, &mut handler_task, &mut user_task) + select3(net_task, handler_task, &mut user_task) .coalesce() .await } diff --git a/src/wireless/thread.rs b/src/wireless/thread.rs index e54c4e7..6b44253 100644 --- a/src/wireless/thread.rs +++ b/src/wireless/thread.rs @@ -1,4 +1,6 @@ use core::pin::pin; +extern crate alloc; +use alloc::boxed::Box; use embassy_futures::select::{select, select3, select4}; use embassy_sync::blocking_mutex::raw::RawMutex; @@ -415,12 +417,13 @@ where NoopWirelessNetCtl::new(NetworkType::Thread), ); - let mut btp_task = pin!(self.0.run_btp(peripheral)); + // Box the largest futures to reduce stack frame size + let btp_task = Box::pin(self.0.run_btp(peripheral)); let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); - let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); + let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); - select(&mut btp_task, &mut handler_task).coalesce().await + select(btp_task, handler_task).coalesce().await } } @@ -452,7 +455,8 @@ where let stack = &mut self.0; - let mut net_task = pin!(stack.run_oper_net( + // Box the largest futures to reduce stack frame size + let net_task = Box::pin(stack.run_oper_net( &net_stack, &netif, &mut mdns, @@ -466,14 +470,14 @@ where let handler = self .0 .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); - let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); + let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); select4( - &mut net_task, + net_task, &mut mgr_task, - &mut handler_task, + handler_task, &mut user_task, ) .coalesce() @@ -507,17 +511,17 @@ where let stack = &mut self.0; - let mut net_task = - pin!(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)); + // Box the largest futures to reduce stack frame size + let net_task = Box::pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)); let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); - let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); + let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); - select3(&mut net_task, &mut handler_task, &mut user_task) + select3(net_task, handler_task, &mut user_task) .coalesce() .await } diff --git a/src/wireless/wifi.rs b/src/wireless/wifi.rs index 206239c..ae34919 100644 --- a/src/wireless/wifi.rs +++ b/src/wireless/wifi.rs @@ -1,4 +1,6 @@ use core::pin::pin; +extern crate alloc; +use alloc::boxed::Box; use embassy_futures::select::{select, select3, select4}; use embassy_sync::blocking_mutex::raw::RawMutex; @@ -450,7 +452,8 @@ where let stack = &mut self.0; - let mut net_task = pin!(stack.run_oper_net( + // Box the largest futures to reduce stack frame size + let net_task = Box::pin(stack.run_oper_net( &net_stack, &netif, &mut mdns, @@ -464,14 +467,14 @@ where let handler = self .0 .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); - let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); + let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); select4( - &mut net_task, + net_task, &mut mgr_task, - &mut handler_task, + handler_task, &mut user_task, ) .coalesce() From f201abce89443d942f8bec7d8b081155beea01af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 08:55:42 +0000 Subject: [PATCH 4/8] Implement bump allocator to eliminate heap allocations in embedded environments Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- libbump_alloc.rlib | Bin 0 -> 34844 bytes src/bump_alloc.rs | 130 +++++++++++++++++++++++++++++++++++++++++++++ src/eth.rs | 107 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 4 files changed, 239 insertions(+) create mode 100644 libbump_alloc.rlib create mode 100644 src/bump_alloc.rs diff --git a/libbump_alloc.rlib b/libbump_alloc.rlib new file mode 100644 index 0000000000000000000000000000000000000000..be3741c7e9c3f87eddbb4c4651445ac8c26e4d99 GIT binary patch literal 34844 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 P#;=NqBaj#a%|HJiotqz) literal 0 HcmV?d00001 diff --git a/src/bump_alloc.rs b/src/bump_alloc.rs new file mode 100644 index 0000000..e866333 --- /dev/null +++ b/src/bump_alloc.rs @@ -0,0 +1,130 @@ +/// A simple bump allocator for fixed-size memory chunks +/// +/// This allocator is designed for async futures that have a known maximum lifetime. +/// All allocations are freed when the allocator is dropped. +use core::alloc::Layout; +use core::cell::Cell; +use core::mem::MaybeUninit; +use core::pin::Pin; +use core::ptr::NonNull; + +/// A bump allocator that uses a provided memory chunk +pub struct BumpAllocator<'a> { + memory: &'a mut [MaybeUninit], + offset: Cell, +} + +impl<'a> BumpAllocator<'a> { + /// Create a new bump allocator with the provided memory chunk + pub fn new(memory: &'a mut [MaybeUninit]) -> Self { + Self { + memory, + offset: Cell::new(0), + } + } + + /// Allocate memory for a future and pin it + pub fn alloc_pin(&mut self, future: F) -> Result>, AllocError> + where + F: core::future::Future, + { + let layout = Layout::new::(); + let ptr = self.alloc_raw(layout)?; + + // Safety: We just allocated the memory and it's properly aligned + unsafe { + let ptr = ptr.as_ptr() as *mut F; + ptr.write(future); + Ok(Pin::new_unchecked(BumpBox { + ptr: NonNull::new_unchecked(ptr), + _allocator: core::marker::PhantomData, + })) + } + } + + fn alloc_raw(&mut self, layout: Layout) -> Result, AllocError> { + let size = layout.size(); + let align = layout.align(); + + let current_offset = self.offset.get(); + + // Align the offset + let aligned_offset = (current_offset + align - 1) & !(align - 1); + let new_offset = aligned_offset + size; + + if new_offset > self.memory.len() { + return Err(AllocError); + } + + self.offset.set(new_offset); + + // Safety: We checked bounds and alignment + let ptr = unsafe { + self.memory.as_mut_ptr().add(aligned_offset) as *mut u8 + }; + + Ok(unsafe { NonNull::new_unchecked(ptr) }) + } + + /// Get the current memory usage + pub fn used(&self) -> usize { + self.offset.get() + } + + /// Get the total capacity + pub fn capacity(&self) -> usize { + self.memory.len() + } +} + +/// A box-like container that uses bump allocation +pub struct BumpBox<'a, T> { + ptr: NonNull, + _allocator: core::marker::PhantomData<&'a ()>, +} + +impl core::ops::Deref for BumpBox<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { self.ptr.as_ref() } + } +} + +impl core::ops::DerefMut for BumpBox<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { self.ptr.as_mut() } + } +} + +impl core::future::Future for BumpBox<'_, T> { + type Output = T::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll { + // Safety: We maintain the pin invariant + let future = unsafe { self.map_unchecked_mut(|s| &mut **s) }; + future.poll(cx) + } +} + +impl Drop for BumpBox<'_, T> { + fn drop(&mut self) { + // Safety: The pointer is valid and we own the data + unsafe { + self.ptr.as_ptr().drop_in_place(); + } + } +} + +/// Error type for allocation failures +#[derive(Debug)] +pub struct AllocError; + +impl core::fmt::Display for AllocError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Bump allocator out of memory") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for AllocError {} \ No newline at end of file diff --git a/src/eth.rs b/src/eth.rs index e435140..7e96698 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -1,9 +1,12 @@ use core::pin::pin; +use core::mem::MaybeUninit; extern crate alloc; use alloc::boxed::Box; use embassy_futures::select::{select, select3}; +use crate::bump_alloc::BumpAllocator; + use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::{GenDiag, NetifDiag}; use rs_matter::dm::clusters::net_comm::NetworkType; @@ -249,6 +252,49 @@ where select(&mut net_task, &mut persist_task).coalesce().await } + /// Run the Matter stack for an Ethernet network with bump allocation. + /// Uses the provided memory buffer for allocations instead of heap. + /// + /// Parameters: + /// - `ethernet` - a user-provided `Ethernet` implementation + /// - `persist` - a user-provided `Persist` implementation + /// - `handler` - a user-provided DM handler implementation + /// - `user` - a user-provided future that will be polled only when the netif interface is up + /// - `memory` - a memory buffer for internal allocations (recommended size: 16KB+) + pub async fn run_with_memory( + &self, + ethernet: N, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: X, + memory: &mut [MaybeUninit], + ) -> Result<(), Error> + where + N: Ethernet, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + X: UserTask, + { + info!("Matter Stack memory: {}B", core::mem::size_of_val(self)); + + let persist = self.create_persist(store); + + persist.load().await?; + + self.matter().reset_transport()?; + + if !self.is_commissioned().await? { + self.matter() + .enable_basic_commissioning(DiscoveryCapabilities::IP, 0) + .await?; // TODO + } + + let mut net_task = pin!(self.run_ethernet_with_memory(ethernet, handler, user, memory)); + let mut persist_task = pin!(self.run_psm(&persist)); + + select(&mut net_task, &mut persist_task).coalesce().await + } + async fn run_ethernet(&self, mut ethernet: N, handler: H, user: X) -> Result<(), Error> where N: Ethernet, @@ -257,6 +303,21 @@ where { Ethernet::run(&mut ethernet, MatterStackEthernetTask(self, handler, user)).await } + + async fn run_ethernet_with_memory( + &self, + mut ethernet: N, + handler: H, + user: X, + memory: &mut [MaybeUninit] + ) -> Result<(), Error> + where + N: Ethernet, + H: AsyncHandler + AsyncMetadata, + X: UserTask, + { + Ethernet::run(&mut ethernet, MatterStackEthernetTaskWithMemory(self, handler, user, memory)).await + } } struct MatterStackEthernetTask<'a, E, H, X>(&'a MatterStack<'a, Eth>, H, X) @@ -265,6 +326,12 @@ where H: AsyncMetadata + AsyncHandler, X: UserTask; +struct MatterStackEthernetTaskWithMemory<'a, E, H, X>(&'a MatterStack<'a, Eth>, H, X, &'a mut [MaybeUninit]) +where + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask; + impl EthernetTask for MatterStackEthernetTask<'_, E, H, X> where E: Embedding + 'static, @@ -298,3 +365,43 @@ where .await } } + +impl EthernetTask for MatterStackEthernetTaskWithMemory<'_, E, H, X> +where + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run(&mut self, net_stack: S, netif: C, mut mdns: M) -> Result<(), Error> + where + S: NetStack, + C: NetifDiag + NetChangeNotif, + M: Mdns, + { + info!("Ethernet driver started with bump allocator"); + + // Create bump allocator from provided memory + let mut allocator = BumpAllocator::new(self.3); + + // Use bump allocator instead of Box::pin for largest futures + let net_task = allocator.alloc_pin(self.0.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None, + )).map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + let handler = self.0.root_handler(&(), &true, &netif, &self.1); + let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + + select3(net_task, handler_task, &mut user_task) + .coalesce() + .await + } +} diff --git a/src/lib.rs b/src/lib.rs index 2be3a17..a5d69ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ #![warn(clippy::large_stack_frames)] #![warn(clippy::large_types_passed_by_value)] +pub mod bump_alloc; + use core::future::Future; use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}; use core::pin::pin; From b8026bb9cfebb902bdeec1477fefd7cd5c7c73dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 08:56:05 +0000 Subject: [PATCH 5/8] Clean up accidentally committed build artifact Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- .gitignore | 1 + libbump_alloc.rlib | Bin 34844 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 libbump_alloc.rlib diff --git a/.gitignore b/.gitignore index cf6578b..041fab0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.vscode /target /Cargo.lock +*.rlib diff --git a/libbump_alloc.rlib b/libbump_alloc.rlib deleted file mode 100644 index be3741c7e9c3f87eddbb4c4651445ac8c26e4d99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34844 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 P#;=NqBaj#a%|HJiotqz) From 6aabfa2a5e3232d1d38c1cf866acb159ab843a17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:23:02 +0000 Subject: [PATCH 6/8] Remove Box::pin usage, make bump allocator generic, implement WiFi bump allocator support Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- src/bump_alloc.rs | 53 ++++----- src/eth.rs | 69 +++++++----- src/wireless.rs | 14 +++ src/wireless/thread.rs | 26 ++--- src/wireless/wifi.rs | 238 +++++++++++++++++++++++++++++++++++++++-- 5 files changed, 325 insertions(+), 75 deletions(-) diff --git a/src/bump_alloc.rs b/src/bump_alloc.rs index e866333..11c0e2e 100644 --- a/src/bump_alloc.rs +++ b/src/bump_alloc.rs @@ -1,6 +1,6 @@ /// A simple bump allocator for fixed-size memory chunks -/// -/// This allocator is designed for async futures that have a known maximum lifetime. +/// +/// This allocator is designed for allocating objects that have a known maximum lifetime. /// All allocations are freed when the allocator is dropped. use core::alloc::Layout; use core::cell::Cell; @@ -22,55 +22,53 @@ impl<'a> BumpAllocator<'a> { offset: Cell::new(0), } } - - /// Allocate memory for a future and pin it - pub fn alloc_pin(&mut self, future: F) -> Result>, AllocError> + + /// Allocate memory for an object and pin it + pub fn alloc_pin(&mut self, object: T) -> Result>, AllocError> where - F: core::future::Future, + T: Sized, { - let layout = Layout::new::(); + let layout = Layout::new::(); let ptr = self.alloc_raw(layout)?; - + // Safety: We just allocated the memory and it's properly aligned unsafe { - let ptr = ptr.as_ptr() as *mut F; - ptr.write(future); + let ptr = ptr.as_ptr() as *mut T; + ptr.write(object); Ok(Pin::new_unchecked(BumpBox { ptr: NonNull::new_unchecked(ptr), _allocator: core::marker::PhantomData, })) } } - + fn alloc_raw(&mut self, layout: Layout) -> Result, AllocError> { let size = layout.size(); let align = layout.align(); - + let current_offset = self.offset.get(); - + // Align the offset let aligned_offset = (current_offset + align - 1) & !(align - 1); let new_offset = aligned_offset + size; - + if new_offset > self.memory.len() { return Err(AllocError); } - + self.offset.set(new_offset); - + // Safety: We checked bounds and alignment - let ptr = unsafe { - self.memory.as_mut_ptr().add(aligned_offset) as *mut u8 - }; - + let ptr = unsafe { self.memory.as_mut_ptr().add(aligned_offset) as *mut u8 }; + Ok(unsafe { NonNull::new_unchecked(ptr) }) } - + /// Get the current memory usage pub fn used(&self) -> usize { self.offset.get() } - + /// Get the total capacity pub fn capacity(&self) -> usize { self.memory.len() @@ -85,7 +83,7 @@ pub struct BumpBox<'a, T> { impl core::ops::Deref for BumpBox<'_, T> { type Target = T; - + fn deref(&self) -> &Self::Target { unsafe { self.ptr.as_ref() } } @@ -99,8 +97,11 @@ impl core::ops::DerefMut for BumpBox<'_, T> { impl core::future::Future for BumpBox<'_, T> { type Output = T::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll { + + fn poll( + self: Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { // Safety: We maintain the pin invariant let future = unsafe { self.map_unchecked_mut(|s| &mut **s) }; future.poll(cx) @@ -127,4 +128,4 @@ impl core::fmt::Display for AllocError { } #[cfg(feature = "std")] -impl std::error::Error for AllocError {} \ No newline at end of file +impl std::error::Error for AllocError {} diff --git a/src/eth.rs b/src/eth.rs index 7e96698..6cd7e25 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -1,7 +1,5 @@ -use core::pin::pin; use core::mem::MaybeUninit; -extern crate alloc; -use alloc::boxed::Box; +use core::pin::pin; use embassy_futures::select::{select, select3}; @@ -305,18 +303,22 @@ where } async fn run_ethernet_with_memory( - &self, - mut ethernet: N, - handler: H, + &self, + mut ethernet: N, + handler: H, user: X, - memory: &mut [MaybeUninit] + memory: &mut [MaybeUninit], ) -> Result<(), Error> where N: Ethernet, H: AsyncHandler + AsyncMetadata, X: UserTask, { - Ethernet::run(&mut ethernet, MatterStackEthernetTaskWithMemory(self, handler, user, memory)).await + Ethernet::run( + &mut ethernet, + MatterStackEthernetTaskWithMemory(self, handler, user, memory), + ) + .await } } @@ -326,7 +328,12 @@ where H: AsyncMetadata + AsyncHandler, X: UserTask; -struct MatterStackEthernetTaskWithMemory<'a, E, H, X>(&'a MatterStack<'a, Eth>, H, X, &'a mut [MaybeUninit]) +struct MatterStackEthernetTaskWithMemory<'a, E, H, X>( + &'a MatterStack<'a, Eth>, + H, + X, + &'a mut [MaybeUninit], +) where E: Embedding + 'static, H: AsyncMetadata + AsyncHandler, @@ -346,8 +353,7 @@ where { info!("Ethernet driver started"); - // Box the largest futures to reduce stack frame size - let net_task = Box::pin(self.0.run_oper_net( + let mut net_task = pin!(self.0.run_oper_net( &net_stack, &netif, &mut mdns, @@ -356,11 +362,11 @@ where )); let handler = self.0.root_handler(&(), &true, &netif, &self.1); - let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); + let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); - select3(net_task, handler_task, &mut user_task) + select3(&mut net_task, &mut handler_task, &mut user_task) .coalesce() .await } @@ -379,26 +385,37 @@ where M: Mdns, { info!("Ethernet driver started with bump allocator"); - + // Create bump allocator from provided memory let mut allocator = BumpAllocator::new(self.3); - - // Use bump allocator instead of Box::pin for largest futures - let net_task = allocator.alloc_pin(self.0.run_oper_net( - &net_stack, - &netif, - &mut mdns, - core::future::pending(), - Option::<(NoNetwork, NoNetwork)>::None, - )).map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + // Use bump allocator instead of stack allocation for largest futures + let net_task = allocator + .alloc_pin(self.0.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None, + )) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false) + })?; let handler = self.0.root_handler(&(), &true, &netif, &self.1); - let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + let handler_task = allocator + .alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false) + })?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); - info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + info!( + "Bump allocator usage: {}/{} bytes", + allocator.used(), + allocator.capacity() + ); select3(net_task, handler_task, &mut user_task) .coalesce() diff --git a/src/wireless.rs b/src/wireless.rs index 901e8f0..d5d071c 100644 --- a/src/wireless.rs +++ b/src/wireless.rs @@ -1,8 +1,11 @@ +use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::{select, select3}; use embassy_sync::blocking_mutex::raw::RawMutex; +use crate::bump_alloc::BumpAllocator; + use rs_matter::dm::clusters::gen_diag::NetifDiag; use rs_matter::dm::clusters::net_comm::NetCtl; use rs_matter::dm::clusters::wifi_diag::WirelessDiag; @@ -336,3 +339,14 @@ where M: RawMutex + Send + Sync + 'static, T: WirelessNetwork, E: Embedding + 'static; + +pub(crate) struct MatterStackWirelessTaskWithMemory<'a, M, T, E, H, U>( + &'a MatterStack<'a, WirelessBle>, + H, + U, + &'a mut [MaybeUninit], +) +where + M: RawMutex + Send + Sync + 'static, + T: WirelessNetwork, + E: Embedding + 'static; diff --git a/src/wireless/thread.rs b/src/wireless/thread.rs index 6b44253..e54c4e7 100644 --- a/src/wireless/thread.rs +++ b/src/wireless/thread.rs @@ -1,6 +1,4 @@ use core::pin::pin; -extern crate alloc; -use alloc::boxed::Box; use embassy_futures::select::{select, select3, select4}; use embassy_sync::blocking_mutex::raw::RawMutex; @@ -417,13 +415,12 @@ where NoopWirelessNetCtl::new(NetworkType::Thread), ); - // Box the largest futures to reduce stack frame size - let btp_task = Box::pin(self.0.run_btp(peripheral)); + let mut btp_task = pin!(self.0.run_btp(peripheral)); let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); - let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); + let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); - select(btp_task, handler_task).coalesce().await + select(&mut btp_task, &mut handler_task).coalesce().await } } @@ -455,8 +452,7 @@ where let stack = &mut self.0; - // Box the largest futures to reduce stack frame size - let net_task = Box::pin(stack.run_oper_net( + let mut net_task = pin!(stack.run_oper_net( &net_stack, &netif, &mut mdns, @@ -470,14 +466,14 @@ where let handler = self .0 .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); - let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); + let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); select4( - net_task, + &mut net_task, &mut mgr_task, - handler_task, + &mut handler_task, &mut user_task, ) .coalesce() @@ -511,17 +507,17 @@ where let stack = &mut self.0; - // Box the largest futures to reduce stack frame size - let net_task = Box::pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)); + let mut net_task = + pin!(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)); let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); - let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); + let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); - select3(net_task, handler_task, &mut user_task) + select3(&mut net_task, &mut handler_task, &mut user_task) .coalesce() .await } diff --git a/src/wireless/wifi.rs b/src/wireless/wifi.rs index ae34919..50e7418 100644 --- a/src/wireless/wifi.rs +++ b/src/wireless/wifi.rs @@ -1,10 +1,11 @@ +use core::mem::MaybeUninit; use core::pin::pin; -extern crate alloc; -use alloc::boxed::Box; use embassy_futures::select::{select, select3, select4}; use embassy_sync::blocking_mutex::raw::RawMutex; +use crate::bump_alloc::BumpAllocator; + use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::GenDiag; use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType}; @@ -26,7 +27,7 @@ use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; use crate::persist::{KvBlobStore, SharedKvBlobStore}; -use crate::wireless::MatterStackWirelessTask; +use crate::wireless::{MatterStackWirelessTask, MatterStackWirelessTaskWithMemory}; use crate::UserTask; use super::{Gatt, GattTask, PreexistingWireless, WirelessMatterStack}; @@ -149,6 +150,43 @@ where select(&mut net_task, &mut persist_task).coalesce().await } + /// Run the Matter stack for a WiFi network with bump allocation. + /// Uses the provided memory buffer for allocations instead of heap. + /// + /// Parameters: + /// - `wifi` - a user-provided WiFi implementation + /// - `store` - a user-provided persistent storage implementation + /// - `handler` - a user-provided DM handler implementation + /// - `user` - a user-provided future that will be polled only when the netif interface is up + /// - `memory` - a memory buffer for internal allocations (recommended size: 16KB+) + pub async fn run_with_memory( + &'static self, + wifi: W, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: U, + memory: &mut [MaybeUninit], + ) -> Result<(), Error> + where + W: Wifi + Gatt, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + info!("Matter Stack memory: {}B", core::mem::size_of_val(self)); + + let persist = self.create_persist(store); + + persist.load().await?; + + self.matter().reset_transport()?; + + let mut net_task = pin!(self.run_wifi_with_memory(wifi, handler, user, memory)); + let mut persist_task = pin!(self.run_psm(&persist)); + + select(&mut net_task, &mut persist_task).coalesce().await + } + async fn run_wifi_coex( &'static self, mut wifi: W, @@ -201,6 +239,45 @@ where } } + async fn run_wifi_with_memory( + &'static self, + mut wifi: W, + handler: H, + mut user: U, + memory: &mut [MaybeUninit], + ) -> Result<(), Error> + where + W: Wifi + Gatt, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + loop { + let commissioned = self.is_commissioned().await?; + + if !commissioned { + self.matter() + .enable_basic_commissioning(DiscoveryCapabilities::BLE, 0) + .await?; // TODO + + Gatt::run( + &mut wifi, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + ) + .await?; + } + + if commissioned { + self.matter().disable_commissioning()?; + } + + Wifi::run( + &mut wifi, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + ) + .await?; + } + } + /// Return a metadata for the root (Endpoint 0) of the Matter Node /// configured for BLE+Wifi network. pub const fn root_endpoint() -> Endpoint<'static> { @@ -452,8 +529,7 @@ where let stack = &mut self.0; - // Box the largest futures to reduce stack frame size - let net_task = Box::pin(stack.run_oper_net( + let mut net_task = pin!(stack.run_oper_net( &net_stack, &netif, &mut mdns, @@ -467,14 +543,14 @@ where let handler = self .0 .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); - let handler_task = Box::pin(self.0.run_handler((&self.1, handler))); + let mut handler_task = pin!(self.0.run_handler((&self.1, handler))); let mut user_task = pin!(self.2.run(&net_stack, &netif)); select4( - net_task, + &mut net_task, &mut mgr_task, - handler_task, + &mut handler_task, &mut user_task, ) .coalesce() @@ -523,3 +599,149 @@ where .await } } + +impl GattTask for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, U> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, +{ + async fn run

(&mut self, peripheral: P) -> Result<(), Error> + where + P: GattPeripheral, + { + let net_ctl = NetCtlWithStatusImpl::new( + &self.0.network.net_state, + NoopWirelessNetCtl::new(NetworkType::Wifi), + ); + + // Create bump allocator from provided memory + let mut allocator = BumpAllocator::new(self.3); + + // Use bump allocator instead of stack allocation for largest futures + let btp_task = allocator.alloc_pin(self.0.run_btp(peripheral)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); + let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + + select(btp_task, handler_task).coalesce().await + } +} + +impl WifiTask for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + WifiDiag + NetChangeNotif, + D: Mdns, + { + info!("WiFi driver started with bump allocator"); + + let mut buf = self.0.network.creds_buf.lock().await; + + let mut mgr = WirelessMgr::new(&self.0.network.networks, &net_ctl, &mut buf); + + let stack = &mut self.0; + + // Create bump allocator from provided memory + let mut allocator = BumpAllocator::new(self.3); + + // Use bump allocator instead of stack allocation for largest futures + let net_task = allocator.alloc_pin(stack.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None + )).map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + let mut mgr_task = pin!(mgr.run()); + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self + .0 + .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); + let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + + select4( + net_task, + &mut mgr_task, + handler_task, + &mut user_task, + ) + .coalesce() + .await + } +} + +impl WifiCoexTask for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + mut gatt: G, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + WifiDiag + NetChangeNotif, + D: Mdns, + G: GattPeripheral, + { + info!("WiFi and BLE drivers started with bump allocator"); + + let stack = &mut self.0; + + // Create bump allocator from provided memory + let mut allocator = BumpAllocator::new(self.3); + + // Use bump allocator instead of stack allocation for largest futures + let net_task = allocator.alloc_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); + let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + + select3(net_task, handler_task, &mut user_task) + .coalesce() + .await + } +} From 1d2075f7524915f6afb238f3e0f42291bac56579 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:30:40 +0000 Subject: [PATCH 7/8] Complete Thread stack bump allocator implementation Co-authored-by: ivmarkov <2607589+ivmarkov@users.noreply.github.com> --- src/eth.rs | 4 +- src/wireless.rs | 2 - src/wireless/thread.rs | 256 ++++++++++++++++++++++++++++++++++++++++- src/wireless/wifi.rs | 89 +++++++++----- 4 files changed, 315 insertions(+), 36 deletions(-) diff --git a/src/eth.rs b/src/eth.rs index 6cd7e25..6e621db 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -399,14 +399,14 @@ where Option::<(NoNetwork, NoNetwork)>::None, )) .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false) + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) })?; let handler = self.0.root_handler(&(), &true, &netif, &self.1); let handler_task = allocator .alloc_pin(self.0.run_handler((&self.1, handler))) .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false) + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) })?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); diff --git a/src/wireless.rs b/src/wireless.rs index d5d071c..76154b8 100644 --- a/src/wireless.rs +++ b/src/wireless.rs @@ -4,8 +4,6 @@ use core::pin::pin; use embassy_futures::select::{select, select3}; use embassy_sync::blocking_mutex::raw::RawMutex; -use crate::bump_alloc::BumpAllocator; - use rs_matter::dm::clusters::gen_diag::NetifDiag; use rs_matter::dm::clusters::net_comm::NetCtl; use rs_matter::dm::clusters::wifi_diag::WirelessDiag; diff --git a/src/wireless/thread.rs b/src/wireless/thread.rs index e54c4e7..9ba86ce 100644 --- a/src/wireless/thread.rs +++ b/src/wireless/thread.rs @@ -1,8 +1,11 @@ +use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::{select, select3, select4}; use embassy_sync::blocking_mutex::raw::RawMutex; +use crate::bump_alloc::BumpAllocator; + use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::GenDiag; use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType}; @@ -24,7 +27,7 @@ use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; use crate::persist::{KvBlobStore, SharedKvBlobStore}; -use crate::wireless::{GattTask, MatterStackWirelessTask}; +use crate::wireless::{GattTask, MatterStackWirelessTask, MatterStackWirelessTaskWithMemory}; use crate::UserTask; use super::{Gatt, PreexistingWireless, WirelessMatterStack}; @@ -147,6 +150,43 @@ where select(&mut net_task, &mut persist_task).coalesce().await } + /// Run the Matter stack for a Thread network with bump allocation. + /// Uses the provided memory buffer for allocations instead of heap. + /// + /// Parameters: + /// - `thread` - a user-provided Thread implementation + /// - `store` - a user-provided persistent storage implementation + /// - `handler` - a user-provided DM handler implementation + /// - `user` - a user-provided future that will be polled only when the netif interface is up + /// - `memory` - a memory buffer for internal allocations (recommended size: 16KB+) + pub async fn run_with_memory( + &'static self, + thread: W, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: U, + memory: &mut [MaybeUninit], + ) -> Result<(), Error> + where + W: Thread + Gatt, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + info!("Matter Stack memory: {}B", core::mem::size_of_val(self)); + + let persist = self.create_persist(store); + + persist.load().await?; + + self.matter().reset_transport()?; + + let mut net_task = pin!(self.run_thread_with_memory(thread, handler, user, memory)); + let mut persist_task = pin!(self.run_psm(&persist)); + + select(&mut net_task, &mut persist_task).coalesce().await + } + async fn run_thread_coex( &'static self, mut thread: W, @@ -201,6 +241,45 @@ where } } + async fn run_thread_with_memory( + &'static self, + mut thread: W, + handler: H, + mut user: U, + memory: &mut [MaybeUninit], + ) -> Result<(), Error> + where + W: Thread + Gatt, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + loop { + let commissioned = self.is_commissioned().await?; + + if !commissioned { + self.matter() + .enable_basic_commissioning(DiscoveryCapabilities::BLE, 0) + .await?; // TODO + + Gatt::run( + &mut thread, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + ) + .await?; + } + + if commissioned { + self.matter().disable_commissioning()?; + } + + Thread::run( + &mut thread, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + ) + .await?; + } + } + /// Return a metadata for the root (Endpoint 0) of the Matter Node /// configured for BLE+Thread network. pub const fn root_endpoint() -> Endpoint<'static> { @@ -522,3 +601,178 @@ where .await } } + +impl GattTask + for MatterStackWirelessTaskWithMemory<'static, M, wireless::Thread, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, +{ + async fn run

(&mut self, peripheral: P) -> Result<(), Error> + where + P: GattPeripheral, + { + let net_ctl = NetCtlWithStatusImpl::new( + &self.0.network.net_state, + NoopWirelessNetCtl::new(NetworkType::Thread), + ); + + // Create bump allocator from provided memory + let mut allocator = BumpAllocator::new(self.3); + + // Use bump allocator instead of stack allocation for largest futures + let btp_task = allocator + .alloc_pin(self.0.run_btp(peripheral)) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + + let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); + let handler_task = allocator + .alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + + info!( + "Bump allocator usage: {}/{} bytes", + allocator.used(), + allocator.capacity() + ); + + select(btp_task, handler_task).coalesce().await + } +} + +impl ThreadTask + for MatterStackWirelessTaskWithMemory<'static, M, wireless::Thread, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + ThreadDiag + NetChangeNotif, + D: Mdns, + { + info!("Thread driver started with bump allocator"); + + let mut buf = self.0.network.creds_buf.lock().await; + + let mut mgr = WirelessMgr::new(&self.0.network.networks, &net_ctl, &mut buf); + + let stack = &mut self.0; + + // Create bump allocator from provided memory + let mut allocator = BumpAllocator::new(self.3); + + // Use bump allocator instead of stack allocation for largest futures + let net_task = allocator + .alloc_pin(stack.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None, + )) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + + let mut mgr_task = pin!(mgr.run()); + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self + .0 + .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); + let handler_task = allocator + .alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!( + "Bump allocator usage: {}/{} bytes", + allocator.used(), + allocator.capacity() + ); + + select4(net_task, &mut mgr_task, handler_task, &mut user_task) + .coalesce() + .await + } +} + +impl ThreadCoexTask + for MatterStackWirelessTaskWithMemory<'static, M, wireless::Thread, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + mut gatt: G, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + ThreadDiag + NetChangeNotif, + D: Mdns, + G: GattPeripheral, + { + info!("Thread and BLE drivers started with bump allocator"); + + let stack = &mut self.0; + + // Create bump allocator from provided memory + let mut allocator = BumpAllocator::new(self.3); + + // Use bump allocator instead of stack allocation for largest futures + let net_task = allocator + .alloc_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); + let handler_task = allocator + .alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!( + "Bump allocator usage: {}/{} bytes", + allocator.used(), + allocator.capacity() + ); + + select3(net_task, handler_task, &mut user_task) + .coalesce() + .await + } +} diff --git a/src/wireless/wifi.rs b/src/wireless/wifi.rs index 50e7418..e25dda5 100644 --- a/src/wireless/wifi.rs +++ b/src/wireless/wifi.rs @@ -619,14 +619,24 @@ where let mut allocator = BumpAllocator::new(self.3); // Use bump allocator instead of stack allocation for largest futures - let btp_task = allocator.alloc_pin(self.0.run_btp(peripheral)) - .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + let btp_task = allocator + .alloc_pin(self.0.run_btp(peripheral)) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); - let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; - - info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + let handler_task = allocator + .alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + + info!( + "Bump allocator usage: {}/{} bytes", + allocator.used(), + allocator.capacity() + ); select(btp_task, handler_task).coalesce().await } @@ -664,14 +674,18 @@ where let mut allocator = BumpAllocator::new(self.3); // Use bump allocator instead of stack allocation for largest futures - let net_task = allocator.alloc_pin(stack.run_oper_net( - &net_stack, - &netif, - &mut mdns, - core::future::pending(), - Option::<(NoNetwork, NoNetwork)>::None - )).map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; - + let net_task = allocator + .alloc_pin(stack.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None, + )) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; + let mut mgr_task = pin!(mgr.run()); let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); @@ -679,25 +693,28 @@ where let handler = self .0 .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); - let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + let handler_task = allocator + .alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); - info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + info!( + "Bump allocator usage: {}/{} bytes", + allocator.used(), + allocator.capacity() + ); - select4( - net_task, - &mut mgr_task, - handler_task, - &mut user_task, - ) - .coalesce() - .await + select4(net_task, &mut mgr_task, handler_task, &mut user_task) + .coalesce() + .await } } -impl WifiCoexTask for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, X> +impl WifiCoexTask + for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, X> where M: RawMutex + Send + Sync + 'static, E: Embedding + 'static, @@ -727,18 +744,28 @@ where let mut allocator = BumpAllocator::new(self.3); // Use bump allocator instead of stack allocation for largest futures - let net_task = allocator.alloc_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) - .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + let net_task = allocator + .alloc_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); - let handler_task = allocator.alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory, false))?; + let handler_task = allocator + .alloc_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| { + rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) + })?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); - info!("Bump allocator usage: {}/{} bytes", allocator.used(), allocator.capacity()); + info!( + "Bump allocator usage: {}/{} bytes", + allocator.used(), + allocator.capacity() + ); select3(net_task, handler_task, &mut user_task) .coalesce() From 89ae13365a4f355491a785c658455f5737187fd8 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 20 Sep 2025 10:59:19 +0000 Subject: [PATCH 8/8] Fix the compilation errors --- examples/light_eth.rs | 12 ++++- src/bump_alloc.rs | 46 ++++++++++---------- src/eth.rs | 74 ++++++++++++++++++++----------- src/wireless.rs | 6 +-- src/wireless/thread.rs | 95 +++++++++++++++++----------------------- src/wireless/wifi.rs | 99 ++++++++++++++++++------------------------ 6 files changed, 167 insertions(+), 165 deletions(-) diff --git a/examples/light_eth.rs b/examples/light_eth.rs index b56ec13..c833bcf 100644 --- a/examples/light_eth.rs +++ b/examples/light_eth.rs @@ -9,7 +9,9 @@ //! //! The example implements a fictitious Light device (an On-Off Matter cluster). #![recursion_limit = "256"] +#![feature(maybe_uninit_uninit_array_transpose)] +use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::select; @@ -18,6 +20,7 @@ use embassy_time::{Duration, Timer}; use env_logger::Target; use log::info; +use rs_matter_stack::bump_alloc::BumpAlloc; use rs_matter_stack::eth::EthMatterStack; use rs_matter_stack::matter::dm::clusters::desc; use rs_matter_stack::matter::dm::clusters::desc::ClusterHandler as _; @@ -77,11 +80,17 @@ fn main() -> Result<(), Error> { Async(desc::DescHandler::new(Dataver::new_rand(stack.matter().rand())).adapt()), ); + static ALLOC_MEM: StaticCell<[u8; 90000]> = StaticCell::new(); + let alloc_mem = ALLOC_MEM.uninit(); + let ptr = alloc_mem.as_mut_ptr() as *mut MaybeUninit; + let p: &'static mut [MaybeUninit] = unsafe { core::slice::from_raw_parts_mut(ptr as _, 90000) }; + let bump_alloc = BumpAlloc::new(p); + // Run the Matter stack with our handler // Using `pin!` is completely optional, but saves some memory due to `rustc` // not being very intelligent w.r.t. stack usage in async functions let store = stack.create_shared_store(DirKvBlobStore::new_default()); - let mut matter = pin!(stack.run_preex( + let mut matter = pin!(stack.run_preex2( // The Matter stack needs UDP sockets to communicate with other Matter devices edge_nal_std::Stack::new(), // Will try to find a default network interface @@ -94,6 +103,7 @@ fn main() -> Result<(), Error> { (NODE, handler), // No user task future to run (), + &bump_alloc, )); // Just for demoing purposes: diff --git a/src/bump_alloc.rs b/src/bump_alloc.rs index 11c0e2e..72339f2 100644 --- a/src/bump_alloc.rs +++ b/src/bump_alloc.rs @@ -1,30 +1,30 @@ -/// A simple bump allocator for fixed-size memory chunks -/// -/// This allocator is designed for allocating objects that have a known maximum lifetime. -/// All allocations are freed when the allocator is dropped. +//! A simple bump allocator for fixed-size memory chunks +//! +//! This allocator is designed for allocating objects that have a known maximum lifetime. +//! All allocations are freed when the allocator is dropped. + use core::alloc::Layout; -use core::cell::Cell; +use core::cell::RefCell; use core::mem::MaybeUninit; use core::pin::Pin; use core::ptr::NonNull; -/// A bump allocator that uses a provided memory chunk -pub struct BumpAllocator<'a> { +struct Inner<'a> { memory: &'a mut [MaybeUninit], - offset: Cell, + offset: usize, } -impl<'a> BumpAllocator<'a> { +/// A bump allocator that uses a provided memory chunk +pub struct BumpAlloc<'a>(RefCell>); + +impl<'a> BumpAlloc<'a> { /// Create a new bump allocator with the provided memory chunk - pub fn new(memory: &'a mut [MaybeUninit]) -> Self { - Self { - memory, - offset: Cell::new(0), - } + pub const fn new(memory: &'a mut [MaybeUninit]) -> Self { + Self(RefCell::new(Inner { memory, offset: 0 })) } /// Allocate memory for an object and pin it - pub fn alloc_pin(&mut self, object: T) -> Result>, AllocError> + pub fn box_pin(&self, object: T) -> Result>, AllocError> where T: Sized, { @@ -42,36 +42,38 @@ impl<'a> BumpAllocator<'a> { } } - fn alloc_raw(&mut self, layout: Layout) -> Result, AllocError> { + fn alloc_raw(&self, layout: Layout) -> Result, AllocError> { let size = layout.size(); let align = layout.align(); - let current_offset = self.offset.get(); + let mut inner = self.0.borrow_mut(); + + let current_offset = inner.offset; // Align the offset let aligned_offset = (current_offset + align - 1) & !(align - 1); let new_offset = aligned_offset + size; - if new_offset > self.memory.len() { + if new_offset > inner.memory.len() { return Err(AllocError); } - self.offset.set(new_offset); + inner.offset = new_offset; // Safety: We checked bounds and alignment - let ptr = unsafe { self.memory.as_mut_ptr().add(aligned_offset) as *mut u8 }; + let ptr = unsafe { inner.memory.as_mut_ptr().add(aligned_offset) as *mut u8 }; Ok(unsafe { NonNull::new_unchecked(ptr) }) } /// Get the current memory usage pub fn used(&self) -> usize { - self.offset.get() + self.0.borrow().offset } /// Get the total capacity pub fn capacity(&self) -> usize { - self.memory.len() + self.0.borrow().memory.len() } } diff --git a/src/eth.rs b/src/eth.rs index 6e621db..0537920 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -1,10 +1,7 @@ -use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::{select, select3}; -use crate::bump_alloc::BumpAllocator; - use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::{GenDiag, NetifDiag}; use rs_matter::dm::clusters::net_comm::NetworkType; @@ -17,6 +14,7 @@ use rs_matter::transport::network::NoNetwork; use rs_matter::utils::init::{init, Init}; use rs_matter::utils::select::Coalesce; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::{Embedding, Network}; @@ -210,6 +208,34 @@ where .await } + pub async fn run_preex2( + &self, + net_stack: U, + netif: N, + mdns: M, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: X, + alloc: &BumpAlloc<'_>, + ) -> Result<(), Error> + where + U: NetStack, + N: NetifDiag + NetChangeNotif, + M: Mdns, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + X: UserTask, + { + self.run_with_memory( + PreexistingEthernet::new(net_stack, netif, mdns), + store, + handler, + user, + alloc, + ) + .await + } + /// Run the Matter stack for an Ethernet network. /// /// Parameters: @@ -265,7 +291,7 @@ where store: &SharedKvBlobStore<'_, S>, handler: H, user: X, - memory: &mut [MaybeUninit], + alloc: &BumpAlloc<'_>, ) -> Result<(), Error> where N: Ethernet, @@ -287,8 +313,9 @@ where .await?; // TODO } - let mut net_task = pin!(self.run_ethernet_with_memory(ethernet, handler, user, memory)); - let mut persist_task = pin!(self.run_psm(&persist)); + let mut net_task = + unwrap!(alloc.box_pin(self.run_ethernet_with_memory(ethernet, handler, user, alloc))); + let mut persist_task = unwrap!(alloc.box_pin(self.run_psm(&persist))); select(&mut net_task, &mut persist_task).coalesce().await } @@ -307,7 +334,7 @@ where mut ethernet: N, handler: H, user: X, - memory: &mut [MaybeUninit], + alloc: &BumpAlloc<'_>, ) -> Result<(), Error> where N: Ethernet, @@ -316,7 +343,7 @@ where { Ethernet::run( &mut ethernet, - MatterStackEthernetTaskWithMemory(self, handler, user, memory), + MatterStackEthernetTaskWithMemory(self, handler, user, alloc), ) .await } @@ -328,11 +355,11 @@ where H: AsyncMetadata + AsyncHandler, X: UserTask; -struct MatterStackEthernetTaskWithMemory<'a, E, H, X>( +struct MatterStackEthernetTaskWithMemory<'a, 'b, E, H, X>( &'a MatterStack<'a, Eth>, H, X, - &'a mut [MaybeUninit], + &'a BumpAlloc<'b>, ) where E: Embedding + 'static, @@ -372,7 +399,7 @@ where } } -impl EthernetTask for MatterStackEthernetTaskWithMemory<'_, E, H, X> +impl EthernetTask for MatterStackEthernetTaskWithMemory<'_, '_, E, H, X> where E: Embedding + 'static, H: AsyncMetadata + AsyncHandler, @@ -386,35 +413,30 @@ where { info!("Ethernet driver started with bump allocator"); - // Create bump allocator from provided memory - let mut allocator = BumpAllocator::new(self.3); - // Use bump allocator instead of stack allocation for largest futures - let net_task = allocator - .alloc_pin(self.0.run_oper_net( + let net_task = self + .3 + .box_pin(self.0.run_oper_net( &net_stack, &netif, &mut mdns, core::future::pending(), Option::<(NoNetwork, NoNetwork)>::None, )) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let handler = self.0.root_handler(&(), &true, &netif, &self.1); - let handler_task = allocator - .alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); info!( "Bump allocator usage: {}/{} bytes", - allocator.used(), - allocator.capacity() + self.3.used(), + self.3.capacity() ); select3(net_task, handler_task, &mut user_task) diff --git a/src/wireless.rs b/src/wireless.rs index 76154b8..457bccb 100644 --- a/src/wireless.rs +++ b/src/wireless.rs @@ -1,4 +1,3 @@ -use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::{select, select3}; @@ -20,6 +19,7 @@ use rs_matter::utils::init::{init, zeroed, Init}; use rs_matter::utils::select::Coalesce; use rs_matter::utils::sync::{blocking, IfMutex}; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::{Embedding, Network}; @@ -338,11 +338,11 @@ where T: WirelessNetwork, E: Embedding + 'static; -pub(crate) struct MatterStackWirelessTaskWithMemory<'a, M, T, E, H, U>( +pub(crate) struct MatterStackWirelessTaskWithMemory<'a, 'b, M, T, E, H, U>( &'a MatterStack<'a, WirelessBle>, H, U, - &'a mut [MaybeUninit], + &'a BumpAlloc<'b>, ) where M: RawMutex + Send + Sync + 'static, diff --git a/src/wireless/thread.rs b/src/wireless/thread.rs index 9ba86ce..94672bc 100644 --- a/src/wireless/thread.rs +++ b/src/wireless/thread.rs @@ -1,11 +1,8 @@ -use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::{select, select3, select4}; use embassy_sync::blocking_mutex::raw::RawMutex; -use crate::bump_alloc::BumpAllocator; - use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::GenDiag; use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType}; @@ -23,6 +20,7 @@ use rs_matter::transport::network::btp::GattPeripheral; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::select::Coalesce; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; @@ -165,7 +163,7 @@ where store: &SharedKvBlobStore<'_, S>, handler: H, user: U, - memory: &mut [MaybeUninit], + alloc: &'static BumpAlloc<'_>, ) -> Result<(), Error> where W: Thread + Gatt, @@ -181,7 +179,7 @@ where self.matter().reset_transport()?; - let mut net_task = pin!(self.run_thread_with_memory(thread, handler, user, memory)); + let mut net_task = pin!(self.run_thread_with_memory(thread, handler, user, alloc)); let mut persist_task = pin!(self.run_psm(&persist)); select(&mut net_task, &mut persist_task).coalesce().await @@ -246,7 +244,7 @@ where mut thread: W, handler: H, mut user: U, - memory: &mut [MaybeUninit], + alloc: &'static BumpAlloc<'_>, ) -> Result<(), Error> where W: Thread + Gatt, @@ -263,7 +261,7 @@ where Gatt::run( &mut thread, - MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), ) .await?; } @@ -274,7 +272,7 @@ where Thread::run( &mut thread, - MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), ) .await?; } @@ -603,7 +601,7 @@ where } impl GattTask - for MatterStackWirelessTaskWithMemory<'static, M, wireless::Thread, E, H, X> + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Thread, E, H, X> where M: RawMutex + Send + Sync + 'static, E: Embedding + 'static, @@ -618,27 +616,22 @@ where NoopWirelessNetCtl::new(NetworkType::Thread), ); - // Create bump allocator from provided memory - let mut allocator = BumpAllocator::new(self.3); - // Use bump allocator instead of stack allocation for largest futures - let btp_task = allocator - .alloc_pin(self.0.run_btp(peripheral)) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let btp_task = self + .3 + .box_pin(self.0.run_btp(peripheral)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); - let handler_task = allocator - .alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; info!( "Bump allocator usage: {}/{} bytes", - allocator.used(), - allocator.capacity() + self.3.used(), + self.3.capacity() ); select(btp_task, handler_task).coalesce().await @@ -646,7 +639,7 @@ where } impl ThreadTask - for MatterStackWirelessTaskWithMemory<'static, M, wireless::Thread, E, H, X> + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Thread, E, H, X> where M: RawMutex + Send + Sync + 'static, E: Embedding + 'static, @@ -674,21 +667,17 @@ where let stack = &mut self.0; - // Create bump allocator from provided memory - let mut allocator = BumpAllocator::new(self.3); - // Use bump allocator instead of stack allocation for largest futures - let net_task = allocator - .alloc_pin(stack.run_oper_net( + let net_task = self + .3 + .box_pin(stack.run_oper_net( &net_stack, &netif, &mut mdns, core::future::pending(), Option::<(NoNetwork, NoNetwork)>::None, )) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let mut mgr_task = pin!(mgr.run()); @@ -697,18 +686,17 @@ where let handler = self .0 .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); - let handler_task = allocator - .alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); info!( "Bump allocator usage: {}/{} bytes", - allocator.used(), - allocator.capacity() + self.3.used(), + self.3.capacity() ); select4(net_task, &mut mgr_task, handler_task, &mut user_task) @@ -718,7 +706,7 @@ where } impl ThreadCoexTask - for MatterStackWirelessTaskWithMemory<'static, M, wireless::Thread, E, H, X> + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Thread, E, H, X> where M: RawMutex + Send + Sync + 'static, E: Embedding + 'static, @@ -744,31 +732,26 @@ where let stack = &mut self.0; - // Create bump allocator from provided memory - let mut allocator = BumpAllocator::new(self.3); - // Use bump allocator instead of stack allocation for largest futures - let net_task = allocator - .alloc_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let net_task = self + .3 + .box_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); - let handler_task = allocator - .alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); info!( "Bump allocator usage: {}/{} bytes", - allocator.used(), - allocator.capacity() + self.3.used(), + self.3.capacity() ); select3(net_task, handler_task, &mut user_task) diff --git a/src/wireless/wifi.rs b/src/wireless/wifi.rs index e25dda5..5676cd9 100644 --- a/src/wireless/wifi.rs +++ b/src/wireless/wifi.rs @@ -1,11 +1,8 @@ -use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::{select, select3, select4}; use embassy_sync::blocking_mutex::raw::RawMutex; -use crate::bump_alloc::BumpAllocator; - use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::GenDiag; use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType}; @@ -23,6 +20,7 @@ use rs_matter::transport::network::btp::GattPeripheral; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::select::Coalesce; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; @@ -165,7 +163,7 @@ where store: &SharedKvBlobStore<'_, S>, handler: H, user: U, - memory: &mut [MaybeUninit], + alloc: &'static BumpAlloc<'_>, ) -> Result<(), Error> where W: Wifi + Gatt, @@ -181,8 +179,9 @@ where self.matter().reset_transport()?; - let mut net_task = pin!(self.run_wifi_with_memory(wifi, handler, user, memory)); - let mut persist_task = pin!(self.run_psm(&persist)); + let mut net_task = + unwrap!(alloc.box_pin(self.run_wifi_with_memory(wifi, handler, user, alloc))); + let mut persist_task = unwrap!(alloc.box_pin(self.run_psm(&persist))); select(&mut net_task, &mut persist_task).coalesce().await } @@ -244,7 +243,7 @@ where mut wifi: W, handler: H, mut user: U, - memory: &mut [MaybeUninit], + alloc: &'static BumpAlloc<'_>, ) -> Result<(), Error> where W: Wifi + Gatt, @@ -261,7 +260,7 @@ where Gatt::run( &mut wifi, - MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), ) .await?; } @@ -272,7 +271,7 @@ where Wifi::run( &mut wifi, - MatterStackWirelessTaskWithMemory(self, &handler, &mut user, memory), + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), ) .await?; } @@ -600,7 +599,8 @@ where } } -impl GattTask for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, U> +impl GattTask + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Wifi, E, H, U> where M: RawMutex + Send + Sync + 'static, E: Embedding + 'static, @@ -615,34 +615,29 @@ where NoopWirelessNetCtl::new(NetworkType::Wifi), ); - // Create bump allocator from provided memory - let mut allocator = BumpAllocator::new(self.3); - // Use bump allocator instead of stack allocation for largest futures - let btp_task = allocator - .alloc_pin(self.0.run_btp(peripheral)) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let btp_task = self + .3 + .box_pin(self.0.run_btp(peripheral)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); - let handler_task = allocator - .alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; info!( "Bump allocator usage: {}/{} bytes", - allocator.used(), - allocator.capacity() + self.3.used(), + self.3.capacity() ); select(btp_task, handler_task).coalesce().await } } -impl WifiTask for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, X> +impl WifiTask for MatterStackWirelessTaskWithMemory<'_, '_, M, wireless::Wifi, E, H, X> where M: RawMutex + Send + Sync + 'static, E: Embedding + 'static, @@ -670,21 +665,17 @@ where let stack = &mut self.0; - // Create bump allocator from provided memory - let mut allocator = BumpAllocator::new(self.3); - // Use bump allocator instead of stack allocation for largest futures - let net_task = allocator - .alloc_pin(stack.run_oper_net( + let net_task = self + .3 + .box_pin(stack.run_oper_net( &net_stack, &netif, &mut mdns, core::future::pending(), Option::<(NoNetwork, NoNetwork)>::None, )) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let mut mgr_task = pin!(mgr.run()); @@ -693,18 +684,17 @@ where let handler = self .0 .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); - let handler_task = allocator - .alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); info!( "Bump allocator usage: {}/{} bytes", - allocator.used(), - allocator.capacity() + self.3.used(), + self.3.capacity() ); select4(net_task, &mut mgr_task, handler_task, &mut user_task) @@ -714,7 +704,7 @@ where } impl WifiCoexTask - for MatterStackWirelessTaskWithMemory<'static, M, wireless::Wifi, E, H, X> + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Wifi, E, H, X> where M: RawMutex + Send + Sync + 'static, E: Embedding + 'static, @@ -740,31 +730,26 @@ where let stack = &mut self.0; - // Create bump allocator from provided memory - let mut allocator = BumpAllocator::new(self.3); - // Use bump allocator instead of stack allocation for largest futures - let net_task = allocator - .alloc_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let net_task = self + .3 + .box_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); - let handler_task = allocator - .alloc_pin(self.0.run_handler((&self.1, handler))) - .map_err(|_| { - rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory) - })?; + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; let mut user_task = pin!(self.2.run(&net_stack, &netif)); info!( "Bump allocator usage: {}/{} bytes", - allocator.used(), - allocator.capacity() + self.3.used(), + self.3.capacity() ); select3(net_task, handler_task, &mut user_task)