Skip to content

Commit a838046

Browse files
DensoADASJoshua HamppJoshua Hamppalsora
authored andcommitted
Feature/available capacity of ipm (ros2#2173)
* added available_capacity to get the lowest number of free capacity for intra-process communication for a publisher Signed-off-by: Joshua Hampp <[email protected]> * added unit tests for available_capacity Signed-off-by: Joshua Hampp <[email protected]> Signed-off-by: Joshua Hampp <[email protected]> * fixed typos in comments Signed-off-by: Joshua Hampp <[email protected]> * Updated warning Co-authored-by: Alberto Soragna <[email protected]> Signed-off-by: Joshua Hampp <[email protected]> * returning 0 if ipm is disabled in lowest_available_ipm_capacity Signed-off-by: Joshua Hampp <[email protected]> * return 0 if no subscribers are present in lowest_available_capacity Signed-off-by: Joshua Hampp <[email protected]> * updated unit test Signed-off-by: Joshua Hampp <[email protected]> * update unit test Signed-off-by: Joshua Hampp <[email protected]> * moved available_capacity to a lambda function to be able to handle subscriptions which went out of scope Signed-off-by: Joshua Hampp <[email protected]> * updated unit test to check subscriptions which went out of scope Signed-off-by: Joshua Hampp <[email protected]> --------- Signed-off-by: Joshua Hampp <[email protected]> Signed-off-by: Joshua Hampp <[email protected]> Co-authored-by: Joshua Hampp <[email protected]> Co-authored-by: Joshua Hampp <[email protected]> Co-authored-by: Alberto Soragna <[email protected]>
1 parent 8b6a033 commit a838046

12 files changed

+316
-0
lines changed

rclcpp/include/rclcpp/experimental/buffers/buffer_implementation_base.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class BufferImplementationBase
3737

3838
virtual void clear() = 0;
3939
virtual bool has_data() const = 0;
40+
virtual size_t available_capacity() const = 0;
4041
};
4142

4243
} // namespace buffers

rclcpp/include/rclcpp/experimental/buffers/intra_process_buffer.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class IntraProcessBufferBase
4545

4646
virtual bool has_data() const = 0;
4747
virtual bool use_take_shared_method() const = 0;
48+
virtual size_t available_capacity() const = 0;
4849
};
4950

5051
template<
@@ -157,6 +158,11 @@ class TypedIntraProcessBuffer : public IntraProcessBuffer<MessageT, Alloc, Messa
157158
return std::is_same<BufferT, MessageSharedPtr>::value;
158159
}
159160

161+
size_t available_capacity() const override
162+
{
163+
return buffer_->available_capacity();
164+
}
165+
160166
private:
161167
std::unique_ptr<BufferImplementationBase<BufferT>> buffer_;
162168

@@ -348,6 +354,11 @@ class ServiceIntraProcessBuffer : public IntraProcessBufferBase
348354
buffer_->clear();
349355
}
350356

357+
size_t available_capacity() const override
358+
{
359+
return buffer_->available_capacity();
360+
}
361+
351362
void add(BufferT && msg)
352363
{
353364
buffer_->enqueue(std::move(msg));

rclcpp/include/rclcpp/experimental/buffers/ring_buffer_implementation.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,18 @@ class RingBufferImplementation : public BufferImplementationBase<BufferT>
160160
return is_full_();
161161
}
162162

163+
/// Get the remaining capacity to store messages
164+
/**
165+
* This member function is thread-safe.
166+
*
167+
* \return the number of free capacity for new messages
168+
*/
169+
size_t available_capacity() const
170+
{
171+
std::lock_guard<std::mutex> lock(mutex_);
172+
return available_capacity_();
173+
}
174+
163175
void clear() override
164176
{
165177
TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
@@ -266,6 +278,17 @@ class RingBufferImplementation : public BufferImplementationBase<BufferT>
266278
return {};
267279
}
268280

281+
/// Get the remaining capacity to store messages
282+
/**
283+
* This member function is not thread-safe.
284+
*
285+
* \return the number of free capacity for new messages
286+
*/
287+
inline size_t available_capacity_() const
288+
{
289+
return capacity_ - size_;
290+
}
291+
269292
size_t capacity_;
270293

271294
std::vector<BufferT> ring_buffer_;

rclcpp/include/rclcpp/experimental/intra_process_manager.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,11 @@ class IntraProcessManager
819819
bool
820820
action_server_is_available(uint64_t ipc_action_client_id);
821821

822+
/// Return the lowest available capacity for all subscription buffers for a publisher id.
823+
RCLCPP_PUBLIC
824+
size_t
825+
lowest_available_capacity(const uint64_t intra_process_publisher_id) const;
826+
822827
private:
823828
struct SplittedSubscriptions
824829
{

rclcpp/include/rclcpp/experimental/subscription_intra_process_base.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ class SubscriptionIntraProcessBase : public rclcpp::Waitable
6666
bool
6767
is_durability_transient_local() const;
6868

69+
virtual
70+
size_t
71+
available_capacity() const = 0;
72+
6973
bool
7074
is_ready(rcl_wait_set_t * wait_set) override = 0;
7175

rclcpp/include/rclcpp/experimental/subscription_intra_process_buffer.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ class SubscriptionIntraProcessBuffer : public SubscriptionROSMsgIntraProcessBuff
179179
return buffer_->use_take_shared_method();
180180
}
181181

182+
size_t available_capacity() const override
183+
{
184+
return buffer_->available_capacity();
185+
}
186+
182187
protected:
183188
void
184189
trigger_guard_condition() override

rclcpp/include/rclcpp/publisher_base.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,17 @@ class PublisherBase : public std::enable_shared_from_this<PublisherBase>
227227
std::vector<rclcpp::NetworkFlowEndpoint>
228228
get_network_flow_endpoints() const;
229229

230+
/// Return the lowest available capacity for all subscription buffers.
231+
/**
232+
* For intraprocess communication return the lowest buffer capacity for all subscriptions.
233+
* If intraprocess is disabled or no intraprocess subscriptions present, return maximum of size_t.
234+
* On failure return 0.
235+
* \return lowest buffer capacity for all subscriptions
236+
*/
237+
RCLCPP_PUBLIC
238+
size_t
239+
lowest_available_ipm_capacity() const;
240+
230241
/// Wait until all published messages are acknowledged or until the specified timeout elapses.
231242
/**
232243
* This method waits until all published messages are acknowledged by all matching

rclcpp/src/rclcpp/intra_process_manager.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,5 +571,53 @@ IntraProcessManager::can_communicate(
571571
return true;
572572
}
573573

574+
size_t
575+
IntraProcessManager::lowest_available_capacity(const uint64_t intra_process_publisher_id) const
576+
{
577+
size_t capacity = std::numeric_limits<size_t>::max();
578+
579+
auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
580+
if (publisher_it == pub_to_subs_.end()) {
581+
// Publisher is either invalid or no longer exists.
582+
RCLCPP_WARN(
583+
rclcpp::get_logger("rclcpp"),
584+
"Calling lowest_available_capacity for invalid or no longer existing publisher id");
585+
return 0u;
586+
}
587+
588+
if (publisher_it->second.take_shared_subscriptions.empty() &&
589+
publisher_it->second.take_ownership_subscriptions.empty())
590+
{
591+
// no subscriptions available
592+
return 0u;
593+
}
594+
595+
auto available_capacity = [this, &capacity](const uint64_t intra_process_subscription_id)
596+
{
597+
auto subscription_it = subscriptions_.find(intra_process_subscription_id);
598+
if (subscription_it != subscriptions_.end()) {
599+
auto subscription = subscription_it->second.lock();
600+
if (subscription) {
601+
capacity = std::min(capacity, subscription->available_capacity());
602+
}
603+
} else {
604+
// Subscription is either invalid or no longer exists.
605+
RCLCPP_WARN(
606+
rclcpp::get_logger("rclcpp"),
607+
"Calling available_capacity for invalid or no longer existing subscription id");
608+
}
609+
};
610+
611+
for (const auto sub_id : publisher_it->second.take_shared_subscriptions) {
612+
available_capacity(sub_id);
613+
}
614+
615+
for (const auto sub_id : publisher_it->second.take_ownership_subscriptions) {
616+
available_capacity(sub_id);
617+
}
618+
619+
return capacity;
620+
}
621+
574622
} // namespace experimental
575623
} // namespace rclcpp

rclcpp/src/rclcpp/publisher_base.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,22 @@ std::vector<rclcpp::NetworkFlowEndpoint> PublisherBase::get_network_flow_endpoin
416416

417417
return network_flow_endpoint_vector;
418418
}
419+
420+
size_t PublisherBase::lowest_available_ipm_capacity() const
421+
{
422+
if (!intra_process_is_enabled_) {
423+
return 0u;
424+
}
425+
426+
auto ipm = weak_ipm_.lock();
427+
428+
if (!ipm) {
429+
// TODO(ivanpauno): should this raise an error?
430+
RCLCPP_WARN(
431+
rclcpp::get_logger("rclcpp"),
432+
"Intra process manager died for a publisher.");
433+
return 0u;
434+
}
435+
436+
return ipm->lowest_available_capacity(intra_process_publisher_id_);
437+
}

rclcpp/test/rclcpp/test_intra_process_buffer.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,75 @@ TEST(TestIntraProcessBuffer, unique_buffer_consume) {
292292
EXPECT_EQ(original_value_2, *unique_data_vec[1]);
293293
EXPECT_NE(original_message_pointer_2, reinterpret_cast<std::uintptr_t>(unique_data_vec[1].get()));
294294
}
295+
296+
/*
297+
Check the available buffer capacity while storing and consuming data from an intra-process
298+
buffer.
299+
The initial available buffer capacity should equal the buffer size.
300+
Inserting a message should decrease the available buffer capacity by 1.
301+
Consuming a message should increase the available buffer capacity by 1.
302+
*/
303+
TEST(TestIntraProcessBuffer, available_capacity) {
304+
using MessageT = char;
305+
using Alloc = std::allocator<void>;
306+
using Deleter = std::default_delete<MessageT>;
307+
using SharedMessageT = std::shared_ptr<const MessageT>;
308+
using UniqueMessageT = std::unique_ptr<MessageT, Deleter>;
309+
using UniqueIntraProcessBufferT = rclcpp::experimental::buffers::TypedIntraProcessBuffer<
310+
MessageT, Alloc, Deleter, UniqueMessageT>;
311+
312+
constexpr auto history_depth = 5u;
313+
314+
auto buffer_impl =
315+
std::make_unique<rclcpp::experimental::buffers::RingBufferImplementation<UniqueMessageT>>(
316+
history_depth);
317+
318+
UniqueIntraProcessBufferT intra_process_buffer(std::move(buffer_impl));
319+
320+
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
321+
322+
auto original_unique_msg = std::make_unique<char>('a');
323+
auto original_message_pointer = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
324+
auto original_value = *original_unique_msg;
325+
326+
intra_process_buffer.add_unique(std::move(original_unique_msg));
327+
328+
EXPECT_EQ(history_depth - 1u, intra_process_buffer.available_capacity());
329+
330+
SharedMessageT popped_shared_msg;
331+
popped_shared_msg = intra_process_buffer.consume_shared();
332+
auto popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_shared_msg.get());
333+
334+
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
335+
EXPECT_EQ(original_value, *popped_shared_msg);
336+
EXPECT_EQ(original_message_pointer, popped_message_pointer);
337+
338+
original_unique_msg = std::make_unique<char>('b');
339+
original_message_pointer = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
340+
original_value = *original_unique_msg;
341+
342+
intra_process_buffer.add_unique(std::move(original_unique_msg));
343+
344+
auto second_unique_msg = std::make_unique<char>('c');
345+
auto second_message_pointer = reinterpret_cast<std::uintptr_t>(second_unique_msg.get());
346+
auto second_value = *second_unique_msg;
347+
348+
intra_process_buffer.add_unique(std::move(second_unique_msg));
349+
350+
EXPECT_EQ(history_depth - 2u, intra_process_buffer.available_capacity());
351+
352+
UniqueMessageT popped_unique_msg;
353+
popped_unique_msg = intra_process_buffer.consume_unique();
354+
popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_unique_msg.get());
355+
356+
EXPECT_EQ(history_depth - 1u, intra_process_buffer.available_capacity());
357+
EXPECT_EQ(original_value, *popped_unique_msg);
358+
EXPECT_EQ(original_message_pointer, popped_message_pointer);
359+
360+
popped_unique_msg = intra_process_buffer.consume_unique();
361+
popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_unique_msg.get());
362+
363+
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
364+
EXPECT_EQ(second_value, *popped_unique_msg);
365+
EXPECT_EQ(second_message_pointer, popped_message_pointer);
366+
}

0 commit comments

Comments
 (0)