Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 100 additions & 27 deletions rmw_fastrtps_shared_cpp/src/custom_publisher_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ bool RMWPublisherEvent::take_event(
{
assert(rmw_fastrtps_shared_cpp::internal::is_event_supported(event_type));

// NOTE: the DataWriter status queries below (get_*_status) acquire the writer's
// internal mutex. Fast-DDS may invoke this publisher's listener callbacks -- which
// take on_new_event_m_ -- while holding that writer mutex. To avoid a lock-order
// inversion (ABBA deadlock) with the writer's threads, on_new_event_m_ must NOT be
// held across a writer get_*_status() call. Each case that queries the writer releases
// the lock around the query, copies into a local, then re-acquires and adopts the
// value only if a listener update has not arrived in the meantime. (Symmetric to the
// subscription-side fix in custom_subscriber_info.cpp.)
rcpputils::unique_lock<std::mutex> lock_mutex(on_new_event_m_);

switch (event_type) {
Expand All @@ -117,7 +125,14 @@ bool RMWPublisherEvent::take_event(
if (liveliness_changed_) {
liveliness_changed_ = false;
} else {
publisher_info_->data_writer_->get_liveliness_lost_status(liveliness_lost_status_);
eprosima::fastdds::dds::LivelinessLostStatus current;
lock_mutex.unlock();
publisher_info_->data_writer_->get_liveliness_lost_status(current);
lock_mutex.lock();
if (!liveliness_changed_) {
liveliness_lost_status_ = current;
}
liveliness_changed_ = false;
}
rmw_data->total_count = liveliness_lost_status_.total_count;
rmw_data->total_count_change = liveliness_lost_status_.total_count_change;
Expand All @@ -130,8 +145,14 @@ bool RMWPublisherEvent::take_event(
if (deadline_changed_) {
deadline_changed_ = false;
} else {
publisher_info_->data_writer_->get_offered_deadline_missed_status(
offered_deadline_missed_status_);
eprosima::fastdds::dds::OfferedDeadlineMissedStatus current;
lock_mutex.unlock();
publisher_info_->data_writer_->get_offered_deadline_missed_status(current);
lock_mutex.lock();
if (!deadline_changed_) {
offered_deadline_missed_status_ = current;
}
deadline_changed_ = false;
}
rmw_data->total_count = offered_deadline_missed_status_.total_count;
rmw_data->total_count_change = offered_deadline_missed_status_.total_count_change;
Expand All @@ -144,8 +165,14 @@ bool RMWPublisherEvent::take_event(
if (incompatible_qos_changed_) {
incompatible_qos_changed_ = false;
} else {
publisher_info_->data_writer_->get_offered_incompatible_qos_status(
incompatible_qos_status_);
eprosima::fastdds::dds::OfferedIncompatibleQosStatus current;
lock_mutex.unlock();
publisher_info_->data_writer_->get_offered_incompatible_qos_status(current);
lock_mutex.lock();
if (!incompatible_qos_changed_) {
incompatible_qos_status_ = current;
}
incompatible_qos_changed_ = false;
}
rmw_data->total_count = incompatible_qos_status_.total_count;
rmw_data->total_count_change = incompatible_qos_status_.total_count_change;
Expand All @@ -157,6 +184,9 @@ bool RMWPublisherEvent::take_event(
break;
case RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE:
{
// get_inconsistent_topic_status() operates on the Topic, not the DataWriter, so
// it does not take the writer mutex and is not part of the writer-mutex inversion;
// left querying under the lock unchanged.
auto rmw_data = static_cast<rmw_incompatible_type_status_t *>(event_info);
if (inconsistent_topic_changed_) {
inconsistent_topic_changed_ = false;
Expand All @@ -174,7 +204,9 @@ bool RMWPublisherEvent::take_event(
auto rmw_data = static_cast<rmw_matched_status_t *>(event_info);

eprosima::fastdds::dds::PublicationMatchedStatus matched_status;
lock_mutex.unlock();
publisher_info_->data_writer_->get_publication_matched_status(matched_status);
lock_mutex.lock();

rmw_data->total_count = static_cast<size_t>(matched_status.total_count);
rmw_data->current_count = static_cast<size_t>(matched_status.current_count);
Expand Down Expand Up @@ -204,52 +236,91 @@ void RMWPublisherEvent::set_on_new_event_callback(
const void * user_data,
rmw_event_callback_t callback)
{
rcpputils::unique_lock<std::mutex> lock_mutex(on_new_event_m_);

// NOTE: like take_event(), this method must not hold on_new_event_m_ across any
// DataWriter call (get_status_mask / get_*_status / set_listener): those acquire the
// writer's internal mutex, and Fast-DDS may invoke this publisher's listener callbacks
// -- which take on_new_event_m_ -- while holding that writer mutex. Holding
// on_new_event_m_ across a writer call is therefore a lock-order inversion (ABBA
// deadlock). The lock is released around every writer query below; each get_*_status
// copies into a local and re-acquires before adopting, keeping any value a listener
// delivered during the unlocked window. get_inconsistent_topic_status() operates on
// the Topic (not the writer) and is left under the lock, as in take_event().
eprosima::fastdds::dds::StatusMask status_mask =
publisher_info_->data_writer_->get_status_mask();

rcpputils::unique_lock<std::mutex> lock_mutex(on_new_event_m_);

if (callback) {
switch (event_type) {
case RMW_EVENT_LIVELINESS_LOST:
publisher_info_->data_writer_->get_liveliness_lost_status(liveliness_lost_status_);
{
eprosima::fastdds::dds::LivelinessLostStatus current;
lock_mutex.unlock();
publisher_info_->data_writer_->get_liveliness_lost_status(current);
lock_mutex.lock();
if (!liveliness_changed_) {
liveliness_lost_status_ = current;
}

if (liveliness_lost_status_.total_count_change > 0) {
callback(user_data, liveliness_lost_status_.total_count_change);
liveliness_lost_status_.total_count_change = 0;
if (liveliness_lost_status_.total_count_change > 0) {
callback(user_data, liveliness_lost_status_.total_count_change);
liveliness_lost_status_.total_count_change = 0;
}
}
break;
case RMW_EVENT_OFFERED_DEADLINE_MISSED:
publisher_info_->data_writer_->get_offered_deadline_missed_status(
offered_deadline_missed_status_);
{
eprosima::fastdds::dds::OfferedDeadlineMissedStatus current;
lock_mutex.unlock();
publisher_info_->data_writer_->get_offered_deadline_missed_status(current);
lock_mutex.lock();
if (!deadline_changed_) {
offered_deadline_missed_status_ = current;
}

if (offered_deadline_missed_status_.total_count_change > 0) {
callback(user_data, offered_deadline_missed_status_.total_count_change);
offered_deadline_missed_status_.total_count_change = 0;
if (offered_deadline_missed_status_.total_count_change > 0) {
callback(user_data, offered_deadline_missed_status_.total_count_change);
offered_deadline_missed_status_.total_count_change = 0;
}
}
break;
case RMW_EVENT_OFFERED_QOS_INCOMPATIBLE:
publisher_info_->data_writer_->get_offered_incompatible_qos_status(
incompatible_qos_status_);
{
eprosima::fastdds::dds::OfferedIncompatibleQosStatus current;
lock_mutex.unlock();
publisher_info_->data_writer_->get_offered_incompatible_qos_status(current);
lock_mutex.lock();
if (!incompatible_qos_changed_) {
incompatible_qos_status_ = current;
}

if (incompatible_qos_status_.total_count_change > 0) {
callback(user_data, incompatible_qos_status_.total_count_change);
incompatible_qos_status_.total_count_change = 0;
if (incompatible_qos_status_.total_count_change > 0) {
callback(user_data, incompatible_qos_status_.total_count_change);
incompatible_qos_status_.total_count_change = 0;
}
}
break;
case RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE:
publisher_info_->data_writer_->get_topic()->get_inconsistent_topic_status(
inconsistent_topic_status_);
if (inconsistent_topic_status_.total_count_change > 0) {
callback(user_data, inconsistent_topic_status_.total_count_change);
inconsistent_topic_status_.total_count_change = 0;
{
// get_inconsistent_topic_status() operates on the Topic, not the DataWriter,
// so it does not take the writer mutex; left querying under the lock.
publisher_info_->data_writer_->get_topic()->get_inconsistent_topic_status(
inconsistent_topic_status_);
if (inconsistent_topic_status_.total_count_change > 0) {
callback(user_data, inconsistent_topic_status_.total_count_change);
inconsistent_topic_status_.total_count_change = 0;
}
}
break;
case RMW_EVENT_PUBLICATION_MATCHED:
{
if (matched_status_.total_count_change > 0) {
callback(user_data, matched_status_.total_count_change);
publisher_info_->data_writer_->get_publication_matched_status(matched_status_);
eprosima::fastdds::dds::PublicationMatchedStatus current;
lock_mutex.unlock();
publisher_info_->data_writer_->get_publication_matched_status(current);
lock_mutex.lock();
matched_status_ = current;
matched_status_.total_count_change = 0;
matched_status_.current_count_change = 0;
}
Expand All @@ -274,6 +345,8 @@ void RMWPublisherEvent::set_on_new_event_callback(
}
}

// set_listener() acquires the writer's internal mutex; release on_new_event_m_ first.
lock_mutex.unlock();
publisher_info_->data_writer_->set_listener(publisher_info_->data_writer_listener_, status_mask);
}

Expand Down
Loading