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

Set explicitly callback type #2059

Merged
merged 3 commits into from
Dec 16, 2022

Conversation

mauropasse
Copy link
Collaborator

@mauropasse mauropasse commented Dec 8, 2022

This is to fix seg-faults happening when compiling with -O2 or -O3 optimizations.
They happened because we did:

auto new_callback =

assuming that auto was deduced to be a std::function<void(size_t)>.

And then, to the trampoline, we say we were passing a function returning void* and with an arg of type size_t

set_on_new_response_callback(
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
static_cast<const void *>(&new_callback));

But when compiling with optimizations, the auto new_callback = ... signature seems to change to other type. So then the trampoline function failed when did the cast back:
auto & actual_callback = *reinterpret_cast<const std::function<ReturnT(Args...)> *>(user_data);

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Copy link
Collaborator

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

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

@mauropasse interesting.

This is to fix seg-faults happening when compiling with -O2 or -O3 optimizations.

could you share the segmentation fault information detail? that might be helpful for other members.

@mauropasse
Copy link
Collaborator Author

This is the backtrace. I think the important part is the #2 where the trampoline does the cast back to std::function<void(size_t)>

#0  std::_Function_handler<void(long unsigned int, int), rclcpp::executors::EventsExecutorEntitiesCollector::create_waitable_callback(void*)::<lambda(size_t, int)> >::_M_invoke(const std::_Any_data &, unsigned long &&, int &&) (
    __functor=..., __args#0=@0x7fffffffdc80: 0, 
    __args#1=<error reading variable: Cannot access memory at address 0x0>)
    at /usr/include/c++/11/bits/std_function.h:290
#1  0x000055555557c959 in std::function<void (unsigned long)>::operator()(unsigned long) const (
    __args#0=<optimized out>, this=<optimized out>) at /usr/include/c++/11/bits/std_function.h:590
#2  rclcpp::detail::cpp_callback_trampoline<void const*, unsigned long, void> (user_data=<optimized out>)
    at /root/callback_issue/install/include/rclcpp/rclcpp/detail/cpp_callback_trampoline.hpp:61
#3  0x00007ffff74e9b7c in PubListener::set_on_new_event_callback(rmw_event_type_e, void const*, void (*)(void const*, unsigned long)) () from /opt/ros/humble/lib/librmw_fastrtps_shared_cpp.so
#4  0x00007ffff74ee44d in rmw_fastrtps_shared_cpp::__rmw_event_set_callback(rmw_event_s*, void (*)(void const*, unsigned long), void const*) () from /opt/ros/humble/lib/librmw_fastrtps_shared_cpp.so
#5  0x00007ffff7ee3472 in rclcpp::QOSEventHandlerBase::set_on_new_event_callback (this=this@entry=0x55555585cff0, 
    callback=callback@entry=0x55555557c930 <rclcpp::detail::cpp_callback_trampoline<void const*, unsigned long, void>(void const*, unsigned long)>, user_data=user_data@entry=0x7fffffffdda0)
    at /root/callback_issue/src/rclcpp/rclcpp/src/rclcpp/qos_event.cpp:79
#6  0x0000555555580a57 in rclcpp::QOSEventHandlerBase::set_on_ready_callback(std::function<void (unsigned long, int)>) (this=this@entry=0x55555585cff0, callback=...)
    at /root/callback_issue/install/include/rclcpp/rclcpp/qos_event.hpp:187

@fujitatomoya
Copy link
Collaborator

rclcpp::detail::cpp_callback_trampoline<void const*, unsigned long, void> (user_data=)

this seems correct to me.

0x000055555557c959 in std::function<void (unsigned long)>::operator()(unsigned long) const (

So does this line.

__args#1=<error reading variable: Cannot access memory at address 0x0>)

callback function can be accessible, but cannot access 1st argument?

any thoughts? @iuhilnehc-ynos

@mauropasse
Copy link
Collaborator Author

Check this example:
https://onlinegdb.com/pdJ6u2gUs

  auto auto_function = [](size_t n){cout << n << endl;};
  std::function<void(size_t)> explicit_function = [](size_t n){cout << n << endl;};

  if (std::is_same<decltype(auto_function), decltype(explicit_function)>::value)
  {
    cout<<"Functions are same type" << endl;
  } else {
    cout<<"Functions are different type" << endl; # This is printed
  }

@fujitatomoya
Copy link
Collaborator

@mauropasse thanks for the study.

(gdb) whatis auto_function
type = <lambda(size_t)>

(gdb) whatis explicit_function
type = std::function<void(long unsigned int)>

(gdb) ptype auto_function
type = struct <lambda(size_t)> {
}

(gdb) ptype explicit_function
type = class std::function<void(long unsigned int)> [with _Signature = void (unsigned long)]
        : public std::_Maybe_unary_or_binary_function<void, unsigned long>, private std::_Function_base {
  private:
    _Invoker_type _M_invoker;

  public:
    function(void);
    function(std::nullptr_t);
    function(const std::function<void(long unsigned int)> &);
    function(std::function<void(long unsigned int)> &&);
    std::function<void(long unsigned int)> & operator=(const std::function<void(long unsigned int)> &);
    std::function<void(long unsigned int)> & operator=(std::function<void(long unsigned int)> &&);
    std::function<void(long unsigned int)> & operator=(std::nullptr_t);
    void swap(std::function<void(long unsigned int)> &);
    operator bool(void) const;
    void operator()(unsigned long) const;
    const std::type_info & target_type(void) const;
    void function<main()::<lambda(size_t)> >(<lambda(size_t)>);

  private:
    typedef void (*_Invoker_type)(const std::_Any_data &, unsigned long &&);
}

reference: https://stackoverflow.com/questions/36030589/i-cannot-pass-lambda-as-stdfunction,

@iuhilnehc-ynos
Copy link
Collaborator

iuhilnehc-ynos commented Dec 12, 2022

This fix seems good to me, but I think it's just a workaround.

According to

* This should allow you to use free functions, lambdas with and without
* captures, and various kinds of std::bind instances.
, I think that the original intention of cpp_callback_trampoline can deal with the lambda.

To cast the lambda to std::function by

auto & actual_callback = *reinterpret_cast<const std::function<ReturnT(Args...)> *>(user_data);
is incorrect.

What I can think of are as follows,

  1. the document should be updated to align with this PR, otherwise someone might use auto again in the future.

  2. or another workaround to add type information to the template, such as

template<
  typename UserDataRealT,
  typename UserDataT,
  typename ... Args,
  typename ReturnT = void
>
ReturnT
cpp_callback_trampoline(UserDataT user_data, Args ... args) noexcept
{
  auto & actual_callback = *static_cast<const UserDataRealT *>(user_data);
  return actual_callback(args ...);
}

...
    set_on_new_event_callback(
      rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
      static_cast<const void *>(&new_callback));

This reverts commit dfb4c54.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
@mauropasse mauropasse force-pushed the mauro/fix-callback-signature branch 3 times, most recently from 155804a to 2d36f0c Compare December 12, 2022 16:45
@mauropasse
Copy link
Collaborator Author

Thanks @fujitatomoya for the further investigation. Cool to see the function types from GDB.
@iuhilnehc-ynos I've reverted the first commit and applied your suggestion, so we still support what the documentation says. Thanks for that!

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Copy link
Collaborator

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

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

@iuhilnehc-ynos 's suggestion makes more sense to me, lgtm.

Copy link
Member

@ivanpauno ivanpauno left a comment

Choose a reason for hiding this comment

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

lgtm!

@fujitatomoya
Copy link
Collaborator

CI:

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

@alsora
Copy link
Collaborator

alsora commented Dec 16, 2022

I'm merging this, since we have 3 approvals and green CI.

@alsora alsora merged commit a20a295 into ros2:rolling Dec 16, 2022
@mauropasse mauropasse deleted the mauro/fix-callback-signature branch December 16, 2022 14:06
mauropasse added a commit to mauropasse/rclcpp that referenced this pull request Dec 16, 2022
* Set explicitly callback type

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Revert "Set explicitly callback type"

This reverts commit dfb4c54.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add type info to cpp_callback_trampoline

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
tonynajjar pushed a commit to pixel-robotics/rclcpp that referenced this pull request Apr 3, 2023
* Set explicitly callback type

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Revert "Set explicitly callback type"

This reverts commit dfb4c54.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add type info to cpp_callback_trampoline

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
tonynajjar pushed a commit to pixel-robotics/rclcpp that referenced this pull request Apr 3, 2023
* Set explicitly callback type

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Revert "Set explicitly callback type"

This reverts commit dfb4c54.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add type info to cpp_callback_trampoline

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
alsora pushed a commit to irobot-ros/rclcpp that referenced this pull request Apr 29, 2023
* Set explicitly callback type

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Revert "Set explicitly callback type"

This reverts commit dfb4c54.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add type info to cpp_callback_trampoline

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
alsora pushed a commit to irobot-ros/rclcpp that referenced this pull request Apr 29, 2023
* Set explicitly callback type

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Revert "Set explicitly callback type"

This reverts commit dfb4c54.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add type info to cpp_callback_trampoline

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
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.

None yet

5 participants