diff --git a/rclrs/src/client.rs b/rclrs/src/client.rs index 266958e6c..33189905c 100644 --- a/rclrs/src/client.rs +++ b/rclrs/src/client.rs @@ -10,8 +10,8 @@ use crate::{ error::ToResult, rcl_bindings::*, MessageCow, Node, RclrsError, RclReturnCode, Promise, ENTITY_LIFECYCLE_MUTEX, - Executable, QoSProfile, Waitable, WaitableLifecycle, - ExecutableHandle, ExecutableKind, ServiceInfo, + RclPrimitive, QoSProfile, Waitable, WaitableLifecycle, + RclPrimitiveHandle, RclPrimitiveKind, ServiceInfo, }; mod client_async_callback; @@ -251,7 +251,7 @@ where board: Arc>> } -impl Executable for ClientExecutable +impl RclPrimitive for ClientExecutable where T: rosidl_runtime_rs::Service, { @@ -259,12 +259,12 @@ where self.board.lock().unwrap().execute(&self.handle) } - fn handle(&self) -> ExecutableHandle { - ExecutableHandle::Client(self.handle.lock()) + fn handle(&self) -> RclPrimitiveHandle { + RclPrimitiveHandle::Client(self.handle.lock()) } - fn kind(&self) -> ExecutableKind { - ExecutableKind::Client + fn kind(&self) -> RclPrimitiveKind { + RclPrimitiveKind::Client } } diff --git a/rclrs/src/client/client_async_callback.rs b/rclrs/src/client/client_async_callback.rs index b97c8472d..e01765f28 100644 --- a/rclrs/src/client/client_async_callback.rs +++ b/rclrs/src/client/client_async_callback.rs @@ -13,7 +13,10 @@ pub trait ClientAsyncCallback: Send + 'static where T: Service, { + /// This represents the type of task (Future) that will be produced by the callback type Task: Future + Send; + + /// Trigger the callback to run fn run_client_async_callback(self, response: T::Response, info: ServiceInfo) -> Self::Task; } diff --git a/rclrs/src/client/client_callback.rs b/rclrs/src/client/client_callback.rs index d34fb6980..037075677 100644 --- a/rclrs/src/client/client_callback.rs +++ b/rclrs/src/client/client_callback.rs @@ -11,6 +11,7 @@ pub trait ClientCallback: Send + 'static where T: Service, { + /// Trigger the callback to run fn run_client_callback(self, response: T::Response, info: ServiceInfo); } diff --git a/rclrs/src/client/client_output.rs b/rclrs/src/client/client_output.rs index e0f7b8c44..3309c1944 100644 --- a/rclrs/src/client/client_output.rs +++ b/rclrs/src/client/client_output.rs @@ -13,13 +13,15 @@ use crate::{ /// /// Users never need to use this trait directly. pub trait ClientOutput: Sized { + /// Create the appropriate type of channel to send the information that the + /// user asked for. fn create_channel() -> (AnyClientOutputSender, Promise); } impl ClientOutput for Response { fn create_channel() -> (AnyClientOutputSender, Promise) { let (sender, receiver) = channel(); - (AnyClientOutputSender::RequestOnly(sender), receiver) + (AnyClientOutputSender::ResponseOnly(sender), receiver) } } @@ -39,8 +41,11 @@ impl ClientOutput for (Response, ServiceInfo) { /// Can send any kind of response for a client call. pub enum AnyClientOutputSender { - RequestOnly(Sender), + /// The user only asked for the response. + ResponseOnly(Sender), + /// The user also asked for the RequestId WithId(Sender<(Response, RequestId)>), + /// The user also asked for the ServiceInfo WithServiceInfo(Sender<(Response, ServiceInfo)>), } @@ -51,17 +56,17 @@ impl AnyClientOutputSender { service_info: rmw_service_info_t, ) { match self { - Self::RequestOnly(sender) => { - sender.send(response); + Self::ResponseOnly(sender) => { + let _ = sender.send(response); } Self::WithId(sender) => { - sender.send(( + let _ = sender.send(( response, RequestId::from_rmw_request_id(&service_info.request_id), )); } Self::WithServiceInfo(sender) => { - sender.send(( + let _ = sender.send(( response, ServiceInfo::from_rmw_service_info(&service_info), )); diff --git a/rclrs/src/executor/basic_executor.rs b/rclrs/src/executor/basic_executor.rs index 89480381b..87b4530d0 100644 --- a/rclrs/src/executor/basic_executor.rs +++ b/rclrs/src/executor/basic_executor.rs @@ -1,17 +1,15 @@ use futures::{ - future::{BoxFuture, FutureExt}, + future::BoxFuture, task::{waker_ref, ArcWake}, channel::{oneshot, mpsc::UnboundedSender}, }; use std::{ - future::Future, sync::{ mpsc::{Sender, Receiver, channel}, atomic::{AtomicBool, Ordering}, Arc, Mutex, }, task::Context as TaskContext, - time::Duration, }; use crate::{ diff --git a/rclrs/src/service.rs b/rclrs/src/service.rs index d3e8c855c..aa990336e 100644 --- a/rclrs/src/service.rs +++ b/rclrs/src/service.rs @@ -8,7 +8,7 @@ use crate::{ error::ToResult, rcl_bindings::*, NodeHandle, RclrsError, Waitable, WaitableLifecycle, QoSProfile, - Executable, ExecutableKind, ExecutableHandle, ENTITY_LIFECYCLE_MUTEX, ExecutorCommands, + RclPrimitive, RclPrimitiveKind, RclPrimitiveHandle, ENTITY_LIFECYCLE_MUTEX, ExecutorCommands, }; mod any_service_callback; @@ -44,6 +44,7 @@ where callback: Arc>>, /// Holding onto this keeps the waiter for this service alive in the wait /// set of the executor. + #[allow(unused)] lifecycle: WaitableLifecycle, } @@ -162,7 +163,7 @@ struct ServiceExecutable { commands: Arc, } -impl Executable for ServiceExecutable +impl RclPrimitive for ServiceExecutable where T: rosidl_runtime_rs::Service, { @@ -174,12 +175,12 @@ where Ok(()) } - fn kind(&self) -> crate::ExecutableKind { - ExecutableKind::Service + fn kind(&self) -> crate::RclPrimitiveKind { + RclPrimitiveKind::Service } - fn handle(&self) -> ExecutableHandle { - ExecutableHandle::Service(self.handle.lock()) + fn handle(&self) -> RclPrimitiveHandle { + RclPrimitiveHandle::Service(self.handle.lock()) } } diff --git a/rclrs/src/subscription.rs b/rclrs/src/subscription.rs index 2c1e8c0ce..ce501ff44 100644 --- a/rclrs/src/subscription.rs +++ b/rclrs/src/subscription.rs @@ -9,8 +9,8 @@ use crate::{ error::ToResult, qos::QoSProfile, rcl_bindings::*, - ExecutorCommands, NodeHandle, RclrsError, Waitable, Executable, ExecutableHandle, - ExecutableKind, WaitableLifecycle, ENTITY_LIFECYCLE_MUTEX, + ExecutorCommands, NodeHandle, RclrsError, Waitable, RclPrimitive, RclPrimitiveHandle, + RclPrimitiveKind, WaitableLifecycle, ENTITY_LIFECYCLE_MUTEX, }; mod any_subscription_callback; @@ -57,6 +57,7 @@ where callback: Arc>>, /// Holding onto this keeps the waiter for this subscription alive in the /// wait set of the executor. + #[allow(unused)] lifecycle: WaitableLifecycle, } @@ -172,7 +173,7 @@ struct SubscriptionExecutable { commands: Arc, } -impl Executable for SubscriptionExecutable +impl RclPrimitive for SubscriptionExecutable where T: Message, { @@ -180,12 +181,12 @@ where self.callback.lock().unwrap().execute(&self.handle, &self.commands) } - fn kind(&self) -> crate::ExecutableKind { - ExecutableKind::Subscription + fn kind(&self) -> crate::RclPrimitiveKind { + RclPrimitiveKind::Subscription } - fn handle(&self) -> ExecutableHandle { - ExecutableHandle::Subscription(self.handle.lock()) + fn handle(&self) -> RclPrimitiveHandle { + RclPrimitiveHandle::Subscription(self.handle.lock()) } } diff --git a/rclrs/src/subscription/any_subscription_callback.rs b/rclrs/src/subscription/any_subscription_callback.rs index 437070912..9a6c97502 100644 --- a/rclrs/src/subscription/any_subscription_callback.rs +++ b/rclrs/src/subscription/any_subscription_callback.rs @@ -50,27 +50,27 @@ impl AnySubscriptionCallback { match self { AnySubscriptionCallback::Regular(cb) => { let (msg, _) = Self::take(handle)?; - commands.run(cb(msg)); + let _ = commands.run(cb(msg)); } AnySubscriptionCallback::RegularWithMessageInfo(cb) => { let (msg, msg_info) = Self::take(handle)?; - commands.run(cb(msg, msg_info)); + let _ = commands.run(cb(msg, msg_info)); } AnySubscriptionCallback::Boxed(cb) => { let (msg, _) = Self::take_boxed(handle)?; - commands.run(cb(msg)); + let _ = commands.run(cb(msg)); } AnySubscriptionCallback::BoxedWithMessageInfo(cb) => { let (msg, msg_info) = Self::take_boxed(handle)?; - commands.run(cb(msg, msg_info)); + let _ = commands.run(cb(msg, msg_info)); } AnySubscriptionCallback::Loaned(cb) => { let (msg, _) = Self::take_loaned(handle)?; - commands.run(cb(msg)); + let _ = commands.run(cb(msg)); } AnySubscriptionCallback::LoanedWithMessageInfo(cb) => { let (msg, msg_info) = Self::take_loaned(handle)?; - commands.run(cb(msg, msg_info)); + let _ = commands.run(cb(msg, msg_info)); } } Ok(()) diff --git a/rclrs/src/wait_set.rs b/rclrs/src/wait_set.rs index d70e311cd..fba917b26 100644 --- a/rclrs/src/wait_set.rs +++ b/rclrs/src/wait_set.rs @@ -26,6 +26,9 @@ use crate::{ mod guard_condition; pub use guard_condition::*; +mod rcl_primitive; +pub use rcl_primitive::*; + mod waitable; pub use waitable::*; @@ -34,7 +37,7 @@ pub use wait_set_runner::*; /// A struct for waiting on subscriptions and other waitable entities to become ready. pub struct WaitSet { - primitives: HashMap>, + primitives: HashMap>, handle: WaitSetHandle, } @@ -70,7 +73,7 @@ impl WaitSet { if entity.in_wait_set() { return Err(RclrsError::AlreadyAddedToWaitSet); } - let kind = entity.executable.kind(); + let kind = entity.primitive.kind(); self.primitives.entry(kind).or_default().push(entity); } self.resize_rcl_containers()?; @@ -116,7 +119,7 @@ impl WaitSet { pub fn wait( &mut self, timeout: Option, - mut f: impl FnMut(&mut dyn Executable) -> Result<(), RclrsError>, + mut f: impl FnMut(&mut dyn RclPrimitive) -> Result<(), RclrsError>, ) -> Result<(), RclrsError> { let timeout_ns = match timeout.map(|d| d.as_nanos()) { None => -1, @@ -153,7 +156,7 @@ impl WaitSet { // the callback for those that were. for waiter in self.primitives.values_mut().flat_map(|v| v) { if waiter.is_ready(&self.handle.rcl_wait_set) { - f(&mut *waiter.executable)?; + f(&mut *waiter.primitive)?; } } diff --git a/rclrs/src/wait_set/guard_condition.rs b/rclrs/src/wait_set/guard_condition.rs index 6c887c9ab..a2dbc2154 100644 --- a/rclrs/src/wait_set/guard_condition.rs +++ b/rclrs/src/wait_set/guard_condition.rs @@ -5,8 +5,8 @@ use std::{ use crate::{ rcl_bindings::*, - ContextHandle, RclrsError, ToResult, WaitableLifecycle, Executable, - Waitable, ExecutableKind, ExecutableHandle, + ContextHandle, RclrsError, ToResult, WaitableLifecycle, RclPrimitive, + Waitable, RclPrimitiveKind, RclPrimitiveHandle, }; /// A waitable entity used for waking up a wait set manually. @@ -135,14 +135,25 @@ struct GuardConditionHandle { /// from rcl and either have static lifetimes or lifetimes that depend on /// something else. pub enum InnerGuardConditionHandle { + /// This variant means the guard condition was created and owned by rclrs. + /// Its memory is managed by us. Owned(rcl_guard_condition_t), + /// This variant means the guard condition was created and owned by rcl. + /// The owner object represents something that the lifecycle of the guard + /// condition depends on, such as the rcl_node that created it. Unowned { + /// This is the unowned guard condition pointer. We must not deallocate + /// it. handle: *const rcl_guard_condition_t, + /// This somehow holds a shared reference to the owner of the guard + /// condition. We need to hold onto this to ensure the guard condition + /// remains valid. owner: Box, }, } impl InnerGuardConditionHandle { + /// Get the handle if it is owned by rclrs pub fn owned(&self) -> Option<&rcl_guard_condition_t> { match self { Self::Owned(handle) => Some(handle), @@ -150,6 +161,7 @@ impl InnerGuardConditionHandle { } } + /// Get the handle if it is owned by rclrs pub fn owned_mut(&mut self) -> Option<&mut rcl_guard_condition_t> { match self { Self::Owned(handle) => Some(handle), @@ -157,6 +169,7 @@ impl InnerGuardConditionHandle { } } + /// Apply a function to the handle pub fn use_handle(&self, f: impl FnOnce(&rcl_guard_condition_t) -> Out) -> Out { match self { Self::Owned(handle) => f(handle), @@ -191,7 +204,7 @@ struct GuardConditionExecutable { callback: Option>, } -impl Executable for GuardConditionExecutable { +impl RclPrimitive for GuardConditionExecutable { fn execute(&mut self) -> Result<(), RclrsError> { if let Some(callback) = &mut self.callback { callback(); @@ -199,12 +212,12 @@ impl Executable for GuardConditionExecutable { Ok(()) } - fn kind(&self) -> ExecutableKind { - ExecutableKind::GuardCondition + fn kind(&self) -> RclPrimitiveKind { + RclPrimitiveKind::GuardCondition } - fn handle(&self) -> ExecutableHandle { - ExecutableHandle::GuardCondition( + fn handle(&self) -> RclPrimitiveHandle { + RclPrimitiveHandle::GuardCondition( self.handle.rcl_guard_condition.lock().unwrap() ) } diff --git a/rclrs/src/wait_set/rcl_primitive.rs b/rclrs/src/wait_set/rcl_primitive.rs new file mode 100644 index 000000000..3fbb172d0 --- /dev/null +++ b/rclrs/src/wait_set/rcl_primitive.rs @@ -0,0 +1,51 @@ +use std::sync::MutexGuard; + +use crate::{ + rcl_bindings::*, + RclrsError, InnerGuardConditionHandle, +}; + +/// This provides the public API for executing a waitable item. +pub trait RclPrimitive: Send + Sync { + /// Trigger this primitive to run. + fn execute(&mut self) -> Result<(), RclrsError>; + + /// Indicate what kind of primitive this is. + fn kind(&self) -> RclPrimitiveKind; + + /// Provide the handle for this primitive + fn handle(&self) -> RclPrimitiveHandle; +} + +/// Enum to describe the kind of an executable. +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum RclPrimitiveKind { + /// Subscription + Subscription, + /// Guard Condition + GuardCondition, + /// Timer + Timer, + /// Client + Client, + /// Service + Service, + /// Event + Event, +} + +/// Used by the wait set to obtain the handle of a primitive. +pub enum RclPrimitiveHandle<'a> { + /// Handle for a subscription + Subscription(MutexGuard<'a, rcl_subscription_t>), + /// Handle for a guard condition + GuardCondition(MutexGuard<'a, InnerGuardConditionHandle>), + /// Handle for a timer + Timer(MutexGuard<'a, rcl_timer_t>), + /// Handle for a client + Client(MutexGuard<'a, rcl_client_t>), + /// Handle for a service + Service(MutexGuard<'a, rcl_service_t>), + /// Handle for an event + Event(MutexGuard<'a, rcl_event_t>), +} diff --git a/rclrs/src/wait_set/waitable.rs b/rclrs/src/wait_set/waitable.rs index f7ba71219..a216a5f7e 100644 --- a/rclrs/src/wait_set/waitable.rs +++ b/rclrs/src/wait_set/waitable.rs @@ -1,132 +1,34 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, - Arc, MutexGuard, + Arc, }; use crate::{ error::ToResult, rcl_bindings::*, - RclrsError, GuardCondition, InnerGuardConditionHandle, + RclrsError, GuardCondition, RclPrimitiveKind, RclPrimitive, RclPrimitiveHandle, }; -/// Enum to describe the kind of an executable. -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum ExecutableKind { - Subscription, - GuardCondition, - Timer, - Client, - Service, - Event, -} - -/// Used by the wait set to obtain the handle of an executable. -pub enum ExecutableHandle<'a> { - Subscription(MutexGuard<'a, rcl_subscription_t>), - GuardCondition(MutexGuard<'a, InnerGuardConditionHandle>), - Timer(MutexGuard<'a, rcl_timer_t>), - Client(MutexGuard<'a, rcl_client_t>), - Service(MutexGuard<'a, rcl_service_t>), - Event(MutexGuard<'a, rcl_event_t>), -} - -#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct WaitableCount { - pub subscriptions: usize, - pub guard_conditions: usize, - pub timers: usize, - pub clients: usize, - pub services: usize, - pub events: usize, -} - -impl WaitableCount { - pub fn new() -> Self { - Self::default() - } - - pub(super) fn add(&mut self, kind: ExecutableKind, count: usize) { - match kind { - ExecutableKind::Subscription => self.subscriptions += count, - ExecutableKind::GuardCondition => self.guard_conditions += count, - ExecutableKind::Timer => self.timers += count, - ExecutableKind::Client => self.clients += count, - ExecutableKind::Service => self.services += count, - ExecutableKind::Event => self.events += count, - } - } - - pub(super) unsafe fn initialize( - &self, - rcl_context: &mut rcl_context_s, - ) -> Result { - unsafe { - // SAFETY: Getting a zero-initialized value is always safe - let mut rcl_wait_set = rcl_get_zero_initialized_wait_set(); - // SAFETY: We're passing in a zero-initialized wait set and a valid context. - // There are no other preconditions. - rcl_wait_set_init( - &mut rcl_wait_set, - self.subscriptions, - self.guard_conditions, - self.timers, - self.clients, - self.services, - self.events, - &mut *rcl_context, - rcutils_get_default_allocator(), - ) - .ok()?; - Ok(rcl_wait_set) - } - } - - pub(super) unsafe fn resize( - &self, - rcl_wait_set: &mut rcl_wait_set_t, - ) -> Result<(), RclrsError> { - unsafe { - rcl_wait_set_resize( - rcl_wait_set, - self.subscriptions, - self.guard_conditions, - self.timers, - self.clients, - self.services, - self.events, - ) - } - .ok() - } -} - -/// This provides the public API for executing a waitable item. -pub trait Executable { - /// Trigger this executable to run. - fn execute(&mut self) -> Result<(), RclrsError>; - - /// Indicate what kind of executable this is. - fn kind(&self) -> ExecutableKind; - - /// Provide the handle for this executable - fn handle(&self) -> ExecutableHandle; -} - +/// This struct manages the presence of an rcl primitive inside the wait set. +/// It will keep track of where the primitive is within the wait set as well as +/// automatically remove the primitive from the wait set once it isn't being +/// used anymore. #[must_use = "If you do not give the Waiter to a WaitSet then it will never be useful"] pub struct Waitable { - pub(super) executable: Box, + pub(super) primitive: Box, in_use: Arc, index_in_wait_set: Option, } impl Waitable { + /// Create a new waitable. pub fn new( - waitable: Box, + primitive: Box, guard_condition: Option>, ) -> (Self, WaitableLifecycle) { let in_use = Arc::new(AtomicBool::new(true)); let waiter = Self { - executable: waitable, + primitive, in_use: Arc::clone(&in_use), index_in_wait_set: None, }; @@ -149,13 +51,13 @@ impl Waitable { // SAFETY: Each field in the wait set is an array of points. // The dereferencing that we do is equivalent to obtaining the // element of the array at the index-th position. - match self.executable.kind() { - ExecutableKind::Subscription => wait_set.subscriptions.add(index).is_null(), - ExecutableKind::GuardCondition => wait_set.guard_conditions.add(index).is_null(), - ExecutableKind::Service => wait_set.services.add(index).is_null(), - ExecutableKind::Client => wait_set.clients.add(index).is_null(), - ExecutableKind::Timer => wait_set.timers.add(index).is_null(), - ExecutableKind::Event => wait_set.events.add(index).is_null(), + match self.primitive.kind() { + RclPrimitiveKind::Subscription => wait_set.subscriptions.add(index).is_null(), + RclPrimitiveKind::GuardCondition => wait_set.guard_conditions.add(index).is_null(), + RclPrimitiveKind::Service => wait_set.services.add(index).is_null(), + RclPrimitiveKind::Client => wait_set.clients.add(index).is_null(), + RclPrimitiveKind::Timer => wait_set.timers.add(index).is_null(), + RclPrimitiveKind::Event => wait_set.events.add(index).is_null(), } }; !ptr_is_null @@ -171,25 +73,25 @@ impl Waitable { unsafe { // SAFETY: The Executable is responsible for maintaining the lifecycle // of the handle, so it is guaranteed to be valid here. - match self.executable.handle() { - ExecutableHandle::Subscription(handle) => { + match self.primitive.handle() { + RclPrimitiveHandle::Subscription(handle) => { rcl_wait_set_add_subscription(wait_set, &*handle, &mut index) } - ExecutableHandle::GuardCondition(handle) => { + RclPrimitiveHandle::GuardCondition(handle) => { handle.use_handle(|handle| { rcl_wait_set_add_guard_condition(wait_set, &*handle, &mut index) }) } - ExecutableHandle::Service(handle) => { + RclPrimitiveHandle::Service(handle) => { rcl_wait_set_add_service(wait_set, &*handle, &mut index) } - ExecutableHandle::Client(handle) => { + RclPrimitiveHandle::Client(handle) => { rcl_wait_set_add_client(wait_set, &*handle, &mut index) } - ExecutableHandle::Timer(handle) => { + RclPrimitiveHandle::Timer(handle) => { rcl_wait_set_add_timer(wait_set, &*handle, &mut index) } - ExecutableHandle::Event(handle) => { + RclPrimitiveHandle::Event(handle) => { rcl_wait_set_add_event(wait_set, &*handle, &mut index) } } @@ -201,6 +103,9 @@ impl Waitable { } } +/// This is used internally to track whether an rcl primitive is still being +/// used. When this gets dropped, the rcl primitive will automatically be +/// removed from the wait set. #[must_use = "If you do not hold onto the WaiterLifecycle, then its Waiter will be immediately dropped"] pub struct WaitableLifecycle { in_use: Arc, @@ -211,7 +116,85 @@ impl Drop for WaitableLifecycle { fn drop(&mut self) { self.in_use.store(false, Ordering::Release); if let Some(guard_condition) = &self.guard_condition { - guard_condition.trigger(); + guard_condition.trigger().ok(); + } + } +} + +/// Count the number of rcl primitives in the wait set. +#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct WaitableCount { + /// How many subscriptions are present + pub subscriptions: usize, + /// How many guard conditions are present + pub guard_conditions: usize, + /// How many timers are present + pub timers: usize, + /// How many clients are present + pub clients: usize, + /// How many services are present + pub services: usize, + /// How many events are present + pub events: usize, +} + +impl WaitableCount { + /// Begin a new count with everything starting out at zero. + pub fn new() -> Self { + Self::default() + } + + pub(super) fn add(&mut self, kind: RclPrimitiveKind, count: usize) { + match kind { + RclPrimitiveKind::Subscription => self.subscriptions += count, + RclPrimitiveKind::GuardCondition => self.guard_conditions += count, + RclPrimitiveKind::Timer => self.timers += count, + RclPrimitiveKind::Client => self.clients += count, + RclPrimitiveKind::Service => self.services += count, + RclPrimitiveKind::Event => self.events += count, } } + + pub(super) unsafe fn initialize( + &self, + rcl_context: &mut rcl_context_s, + ) -> Result { + unsafe { + // SAFETY: Getting a zero-initialized value is always safe + let mut rcl_wait_set = rcl_get_zero_initialized_wait_set(); + // SAFETY: We're passing in a zero-initialized wait set and a valid context. + // There are no other preconditions. + rcl_wait_set_init( + &mut rcl_wait_set, + self.subscriptions, + self.guard_conditions, + self.timers, + self.clients, + self.services, + self.events, + &mut *rcl_context, + rcutils_get_default_allocator(), + ) + .ok()?; + Ok(rcl_wait_set) + } + } + + pub(super) unsafe fn resize( + &self, + rcl_wait_set: &mut rcl_wait_set_t, + ) -> Result<(), RclrsError> { + unsafe { + rcl_wait_set_resize( + rcl_wait_set, + self.subscriptions, + self.guard_conditions, + self.timers, + self.clients, + self.services, + self.events, + ) + } + .ok() + } }