Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loaned sample zero-copy API support #297

Merged
merged 16 commits into from
Apr 8, 2021

Conversation

sumanth-nirmal
Copy link
Contributor

This PR adds support for the zero-copy API.

@sumanth-nirmal sumanth-nirmal force-pushed the add-loaned-msg-support branch 4 times, most recently from a8fd9dc to 348985a Compare April 5, 2021 19:56
@sumanth-nirmal sumanth-nirmal changed the base branch from iceoryx to master April 6, 2021 22:02
@sumanth-nirmal sumanth-nirmal marked this pull request as ready for review April 6, 2021 22:02
@eboasson
Copy link
Collaborator

eboasson commented Apr 7, 2021

I have only had a quick look for now, but there clearly is an issue if a serdata is constructed from a loan (whether or not Iceoryx), and Cyclone then needs to invoke one of the operations that accesses the serialized representation. For example, the to_ser function returns a pointer in the serialized representation, and to_sample deserializes the data.

Something like the below would do, it basically performs the serialization on-demand. It is clearly broken because: (1) there are more functions that access it, and (2) there is always the possibility of multiple threads forcing the serialization at the same time. I would address (1) by going over the code and doing the serialization wherever it is needed 🙃 and address (2) by checking whether data is set, serializing into a new buffer if not, and then atomically setting data (if not yet set) or discarding that new buffer (if another thread raced us and won).

diff --git a/rmw_cyclonedds_cpp/src/serdata.cpp b/rmw_cyclonedds_cpp/src/serdata.cpp
index 496da75a..a5c7a84e 100644
--- a/rmw_cyclonedds_cpp/src/serdata.cpp
+++ b/rmw_cyclonedds_cpp/src/serdata.cpp
@@ -181,15 +181,11 @@ static struct ddsi_serdata * serdata_rmw_from_keyhash(
   return new serdata_rmw(type, SDK_KEY);
 }
 
-static struct ddsi_serdata * serdata_rmw_from_sample(
-  const struct ddsi_sertype * typecmn,
-  enum ddsi_serdata_kind kind,
-  const void * sample)
+static void serdata_rmw_serialize_into(serdata_rmw *d, const void *sample)
 {
+  const struct sertype_rmw * type = static_cast<const struct sertype_rmw *>(d->type);
   try {
-    const struct sertype_rmw * type = static_cast<const struct sertype_rmw *>(typecmn);
-    auto d = std::make_unique<serdata_rmw>(type, kind);
-    if (kind != SDK_DATA) {
+    if (d->kind != SDK_DATA) {
       /* ROS 2 doesn't do keys, so SDK_KEY is trivial */
     } else if (!type->is_request_header) {
       size_t sz = type->cdr_writer->get_serialized_size(sample);
@@ -204,8 +200,21 @@ static struct ddsi_serdata * serdata_rmw_from_sample(
       d->resize(sz);
       type->cdr_writer->serialize(d->data(), wrap);
     }
-    return d.release();
+  } catch (std::exception & e) {
+    RMW_SET_ERROR_MSG(e.what());
+  }
+}
 
+static struct ddsi_serdata * serdata_rmw_from_sample(
+  const struct ddsi_sertype * typecmn,
+  enum ddsi_serdata_kind kind,
+  const void * sample)
+{
+  try {
+    const struct sertype_rmw * type = static_cast<const struct sertype_rmw *>(typecmn);
+    auto d = std::make_unique<serdata_rmw>(type, kind);
+    serdata_rmw_serialize_into(d.get(), sample);
+    return d.release();
   } catch (std::exception & e) {
     RMW_SET_ERROR_MSG(e.what());
     return nullptr;
@@ -214,7 +223,7 @@ static struct ddsi_serdata * serdata_rmw_from_sample(
 
 static struct ddsi_serdata * serdata_rmw_from_iox(
   const struct ddsi_sertype * typecmn,
-  enum  ddsi_serdata_kind kind, iox_sub_t * sub, void * iox_buffer)
+  enum  ddsi_serdata_kind kind, void * sub, void * iox_buffer)
 {
   static_cast<void>(sub);  // unused
   const struct sertype_rmw * type = static_cast<const struct sertype_rmw *>(typecmn);
@@ -252,6 +261,10 @@ static struct ddsi_serdata * serdata_rmw_to_untyped(const struct ddsi_serdata *
 static void serdata_rmw_to_ser(const struct ddsi_serdata * dcmn, size_t off, size_t sz, void * buf)
 {
   auto d = static_cast<const serdata_rmw *>(dcmn);
+  if (d->iox_chunk && d->data() == nullptr) {
+    // FIXME: this needs to protected so it doesn't happen multiple times
+    serdata_rmw_serialize_into(const_cast<serdata_rmw *>(d), SHIFT_PAST_ICEORYX_HEADER(d->iox_chunk));
+  }
   memcpy(buf, byte_offset(d->data(), off), sz);
 }
 
@@ -260,6 +273,10 @@ static struct ddsi_serdata * serdata_rmw_to_ser_ref(
   size_t sz, ddsrt_iovec_t * ref)
 {
   auto d = static_cast<const serdata_rmw *>(dcmn);
+  if (d->iox_chunk && d->data() == nullptr) {
+    // FIXME: this needs to protected so it doesn't happen multiple times
+    serdata_rmw_serialize_into(const_cast<serdata_rmw *>(d), SHIFT_PAST_ICEORYX_HEADER(d->iox_chunk));
+  }
   ref->iov_base = byte_offset(d->data(), off);
   ref->iov_len = (ddsrt_iov_len_t) sz;
   return ddsi_serdata_ref(d);

  - Remove storing of the iox_chunk in rmw and update it directly in ddsi_serdata
  - Use write_cdr/take_cdr with zero-copy path

Signed-off-by: Sumanth Nirmal <[email protected]>
@sumanth-nirmal sumanth-nirmal force-pushed the add-loaned-msg-support branch 4 times, most recently from 1467a42 to fe08c61 Compare April 7, 2021 21:36
@sumanth-nirmal sumanth-nirmal force-pushed the add-loaned-msg-support branch from fe08c61 to 9dd7f2b Compare April 7, 2021 21:43
@clalancette
Copy link
Contributor

Initial CI, just up to rmw_cyclonedds_cpp:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

(assuming this is happy, I'll do a full CI to make sure nothing downstream got broken)

@clalancette
Copy link
Contributor

All right, straightforward CI is happy. Here is full one:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

Copy link
Contributor

@clalancette clalancette left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't say I love the #ifdef jungle, but we can fix that up later.

I've got one comment inline about reducing code duplication, but that can be follow-up work.

I'm going to approve this, but the following needs to happen to merge:

  1. CI needs to come back green.
  2. Open an issue to fix up the follow-up items: removing the ifdef jungle, and reducing code duplication.
  3. We need to figure out what the situation is with the branches on cyclonedds (that is, whether we should be building this against the master branch of cyclonedds or the iceoryx branch. If we are doing the latter, we also need an update to https://github.com/ros2/ros2/blob/master/ros2.repos to change branches).
  4. I'd really prefer if @eboasson gives his approval on this.

Comment on lines +30 to +47
const rosidl_message_type_support_t * ts = get_message_typesupport_handle(
type_supports, rosidl_typesupport_introspection_cpp::typesupport_identifier);
if (ts != nullptr) {
auto members =
static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(ts->data);
return members->size_of_;
} else {
// handle C Typesupport
const rosidl_message_type_support_t * ts_c = get_message_typesupport_handle(
type_supports, rosidl_typesupport_introspection_c__identifier);
if (ts_c != nullptr) {
auto members =
static_cast<const rosidl_typesupport_introspection_c__MessageMembers *>(ts_c->data);
return members->size_of_;
} else {
throw std::runtime_error("get_message_size, unsupported typesupport");
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern of:

ts = get_typesupport(C++);
if (ts != nullptr) {
    do_c++_thing;
} else {
  ts = get_typesupport(C);
  do_C_thing;
}

Is really common in the codebase. It would be really nice to come up with a common method for this (maybe that takes a std::function to "do-the-thing"?).

@clalancette
Copy link
Contributor

@sumanth-nirmal It looks like we are getting segfaults in some tests on aarch64. Can you take a look?

…urn RMW_RET_UNSUPPORTED

Signed-off-by: Sumanth Nirmal <[email protected]>
@sumanth-nirmal
Copy link
Contributor Author

@sumanth-nirmal It looks like we are getting segfaults in some tests on aarch64. Can you take a look?

@clalancette I pushed the changes, can we start a new CI pipeline?

@clalancette
Copy link
Contributor

CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@eboasson
Copy link
Collaborator

eboasson commented Apr 8, 2021

@clalancette I am in agreement with your review notes. It is not polished yet, but it works and any shortcomings can be dealt with in the coming days. So it has my approval (pending the same items that you listed).

We need to figure out what the situation is with the branches on cyclonedds (that is, whether we should be building this against the master branch of cyclonedds or the iceoryx branch. If we are doing the latter, we also need an update to https://github.com/ros2/ros2/blob/master/ros2.repos to change branches).

It is meant to be used with cyclonedds' iceoryx branch, which is on a path to become the 0.8.0 release, and which gives the actual possibility of transparently delivering data via Iceoryx. The reason why I checked against both branches of cyclonedds is that it should work with both (it does), and that in turn means keeping track of branches and what-works-with-what is kept nice and simple.

@clalancette
Copy link
Contributor

CI (just up to test_communication, rclcpp, rmw_implementation, rcl, and rcl_action):

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@clalancette
Copy link
Contributor

All right, green CI there (the macOS failure is a known flakey test). Here's the whole thing:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@sumanth-nirmal sumanth-nirmal mentioned this pull request Apr 8, 2021
3 tasks
@clalancette
Copy link
Contributor

All right, all is green. Going ahead and merging this one and ros2/ros2#1126

@clalancette clalancette merged commit 51a8b3c into ros2:master Apr 8, 2021
eboasson pushed a commit to eboasson/rmw_cyclonedds that referenced this pull request May 17, 2021
* Add an API to determine if a type is self contained

Add helper functions for getting message size and for ini, fini of message
Add additional serdata_ops conditionally when SHM is enabled
Update sertype initialization flags with msg fixed type information
Update serdata_rmw to hold iceoryx chunk pointer
Update to_sample and from_sample for zero-copy path
Update Publish path for the zero-copy loaned API
Update Subscription path for the zero-copy loaned API
Avoid using of from_sample and to_sample in the zero-copy path
Update sertype init flags with fixed type
Fix the serdata_rmw_from_iox signature
Update can_loan_messages only when the loaning is possible from middleware
Enable loaned message APIs only if the DDS supports it
Updates checks for loaning, if the loaning is not supported, then return RMW_RET_UNSUPPORTED
Use extracted message typesupport for checking if a type is fixed

Signed-off-by: Sumanth Nirmal <[email protected]>
clalancette pushed a commit to eboasson/rmw_cyclonedds that referenced this pull request May 18, 2022
* Add an API to determine if a type is self contained

Add helper functions for getting message size and for ini, fini of message
Add additional serdata_ops conditionally when SHM is enabled
Update sertype initialization flags with msg fixed type information
Update serdata_rmw to hold iceoryx chunk pointer
Update to_sample and from_sample for zero-copy path
Update Publish path for the zero-copy loaned API
Update Subscription path for the zero-copy loaned API
Avoid using of from_sample and to_sample in the zero-copy path
Update sertype init flags with fixed type
Fix the serdata_rmw_from_iox signature
Update can_loan_messages only when the loaning is possible from middleware
Enable loaned message APIs only if the DDS supports it
Updates checks for loaning, if the loaning is not supported, then return RMW_RET_UNSUPPORTED
Use extracted message typesupport for checking if a type is fixed

Signed-off-by: Sumanth Nirmal <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants