Skip to content

Conversation

@bpapaspyros
Copy link

@bpapaspyros bpapaspyros commented Jan 5, 2026

Description

This PR solves the issue by partially duplicating the existing functions but omitting the mutex locks. We can do nicer work without duplicating much, but that would require changing the original functions a bit, so ultimately I think duplicating this much is not a problem.

Review guidelines

Estimated Time of Review: 5 minutes

Checklist before merging:

  • Confirm that the relevant changelog(s) are up-to-date in case of any user-facing changes

Related issues

Blocked by:

@bpapaspyros bpapaspyros linked an issue Jan 5, 2026 that may be closed by this pull request
@bpapaspyros bpapaspyros self-assigned this Jan 5, 2026
Copy link
Member

@domire8 domire8 left a comment

Choose a reason for hiding this comment

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

Seems like a reasonable change to me!

Copy link
Member

@domire8 domire8 left a comment

Choose a reason for hiding this comment

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

Looks good to me!

SprGrf
SprGrf previously approved these changes Jan 5, 2026
Copy link

@SprGrf SprGrf left a comment

Choose a reason for hiding this comment

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

Nice!

Base automatically changed from feat/control-type-change to main January 13, 2026 10:03
@domire8 domire8 dismissed SprGrf’s stale review January 13, 2026 10:03

The base branch was changed.

@bpapaspyros bpapaspyros marked this pull request as ready for review January 13, 2026 10:58
domire8
domire8 previously approved these changes Jan 13, 2026
Copy link
Member

@eeberhard eeberhard left a comment

Choose a reason for hiding this comment

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

justification for the lockfree service is fine, just some suggestions for clarity and code duplication

Copy link
Member

@eeberhard eeberhard left a comment

Choose a reason for hiding this comment

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

this is certainly more compact and smarter than my suggestion, which was only concerned with the callback part (so yes would have required 4x validate_service_name and get_node()->create_service as in the details block below), but at the same time doesn't need any templating.

4x implementation

void BaseControllerInterface::add_service(
    const std::string& service_name, const std::function<ControllerServiceResponse(void)>& callback) {
  auto parsed_service_name = validate_service_name(service_name, "empty");
  if (!parsed_service_name.empty()) {
    try {
      auto service = get_node()->create_service<modulo_interfaces::srv::EmptyTrigger>(
          "~/" + parsed_service_name,
          [this, callback](
              const std::shared_ptr<modulo_interfaces::srv::EmptyTrigger::Request>,
              std::shared_ptr<modulo_interfaces::srv::EmptyTrigger::Response> response) {
            auto r = handle_service_callback(callback, true);
            response->success = r.success;
            response->message = r.message;
          },
          qos_);
      empty_services_.insert_or_assign(parsed_service_name, service);
    } catch (const std::exception& ex) {
      RCLCPP_ERROR(get_node()->get_logger(), "Failed to add service '%s': %s", parsed_service_name.c_str(), ex.what());
    }
  }

void BaseControllerInterface::add_service(
    const std::string& service_name,const std::function<ControllerServiceResponse(const std::string& string)>& callback) {
  auto parsed_service_name = validate_service_name(service_name, "string");
  if (!parsed_service_name.empty()) {
    try {
      auto service = get_node()->create_service<modulo_interfaces::srv::StringTrigger>(
          "~/" + parsed_service_name,
          [this, callback](
              const std::shared_ptr<modulo_interfaces::srv::StringTrigger::Request> request,
              std::shared_ptr<modulo_interfaces::srv::StringTrigger::Response> response) {
            const auto run_callback = [&]() { return callback(request->payload); };
            auto r = handle_service_callback(run_callback, true);
            response->success = r.success;
            response->message = r.message;
          },
          qos_);
      empty_services_.insert_or_assign(parsed_service_name, service);
    } catch (const std::exception& ex) {
      RCLCPP_ERROR(get_node()->get_logger(), "Failed to add service '%s': %s", parsed_service_name.c_str(), ex.what());
    }
  }

// and again x2 for lockfree, acquire_lock false

Overall if your latest implementation is non-breaking and functions as before then seems an improvement to me.

Copy link
Member

@domire8 domire8 left a comment

Choose a reason for hiding this comment

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

This looks indeed non breaking, tested with the tags from v5.0.1 Core but built with this modulo version instead of v5.2.2. If anything was broken, then JTC should have complained. This is the best test that I can think of.

@bpapaspyros bpapaspyros merged commit e6d9757 into main Jan 13, 2026
4 checks passed
@bpapaspyros bpapaspyros deleted the feat/lock-free-service branch January 13, 2026 14:31
@github-actions github-actions bot locked and limited conversation to collaborators Jan 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement lock-free service for controllers

4 participants