pw_rpc: Use pw::IntrusiveList for services

Replace the ServiceRegistry and custom intrusive list logic with
pw::IntrusiveList.

Change-Id: Id2b01730593564d167795edd9506a9aa750b45a5
diff --git a/pw_rpc/BUILD b/pw_rpc/BUILD
index ac70d4f..7cf984f 100644
--- a/pw_rpc/BUILD
+++ b/pw_rpc/BUILD
@@ -28,10 +28,8 @@
         "base_server_writer.cc",
         "public/pw_rpc/internal/base_server_writer.h",
         "public/pw_rpc/internal/service.h",
-        "public/pw_rpc/internal/service_registry.h",
         "server.cc",
         "service.cc",
-        "service_registry.cc",
     ],
     hdrs = [
         "public/pw_rpc/server.h",
diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn
index 28aedf6..9ef034b 100644
--- a/pw_rpc/BUILD.gn
+++ b/pw_rpc/BUILD.gn
@@ -40,11 +40,8 @@
   pw_source_set(_target_name) {
     forward_variables_from(invoker, "*")
 
-    public_configs = [ ":default_config" ]
     public_deps = [
-      ":common",
-      dir_pw_span,
-      dir_pw_status,
+      ":server_library_deps",
       implementation,
     ]
     deps = [
@@ -59,10 +56,8 @@
       "base_server_writer.cc",
       "public/pw_rpc/internal/base_server_writer.h",
       "public/pw_rpc/internal/service.h",
-      "public/pw_rpc/internal/service_registry.h",
       "server.cc",
       "service.cc",
-      "service_registry.cc",
     ]
     allow_circular_includes_from = [ implementation ]
     friend = [ "./*" ]
@@ -80,6 +75,19 @@
   }
 }
 
+# Put these dependencies into a group since they need to be shared by the server
+# library and its implementation library.
+group("server_library_deps") {
+  public_configs = [ ":default_config" ]
+  public_deps = [
+    ":common",
+    "$dir_pw_containers:intrusive_list",
+    dir_pw_span,
+    dir_pw_status,
+  ]
+  visibility = [ "./*" ]
+}
+
 # Classes with no dependencies on the protobuf library for method invocations.
 pw_source_set("common") {
   public_configs = [ ":default_config" ]
diff --git a/pw_rpc/nanopb/BUILD.gn b/pw_rpc/nanopb/BUILD.gn
index a8916f4..a8b0cc8 100644
--- a/pw_rpc/nanopb/BUILD.gn
+++ b/pw_rpc/nanopb/BUILD.gn
@@ -27,12 +27,7 @@
   public_configs = [ ":config" ]
   public = [ "public_overrides/pw_rpc/internal/method.h" ]
   sources = [ "method.cc" ]
-  public_deps = [
-    "../:common",
-    dir_pw_assert,
-    dir_pw_span,
-    dir_pw_status,
-  ]
+  public_deps = [ "..:server_library_deps" ]
 
   if (dir_pw_third_party_nanopb != "") {
     public_deps += [ dir_pw_third_party_nanopb ]
diff --git a/pw_rpc/public/pw_rpc/internal/service.h b/pw_rpc/public/pw_rpc/internal/service.h
index fb42585..7e5a96f 100644
--- a/pw_rpc/public/pw_rpc/internal/service.h
+++ b/pw_rpc/public/pw_rpc/internal/service.h
@@ -16,6 +16,7 @@
 #include <cstdint>
 #include <utility>
 
+#include "pw_containers/intrusive_list.h"
 #include "pw_rpc/internal/method.h"
 #include "pw_span/span.h"
 
@@ -23,7 +24,7 @@
 
 // Base class for all RPC services. This cannot be instantiated directly; use a
 // generated subclass instead.
-class Service {
+class Service : public IntrusiveList<Service>::Item {
  public:
   uint32_t id() const { return id_; }
 
@@ -33,14 +34,13 @@
  protected:
   template <typename T>
   constexpr Service(uint32_t id, T&& methods)
-      : id_(id), methods_(std::forward<T>(methods)), next_(nullptr) {}
+      : id_(id), methods_(std::forward<T>(methods)) {}
 
  private:
   friend class ServiceRegistry;
 
   uint32_t id_;
   span<const Method> methods_;
-  Service* next_;
 };
 
 }  // namespace pw::rpc::internal
diff --git a/pw_rpc/public/pw_rpc/internal/service_registry.h b/pw_rpc/public/pw_rpc/internal/service_registry.h
deleted file mode 100644
index 283a36c..0000000
--- a/pw_rpc/public/pw_rpc/internal/service_registry.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-#pragma once
-
-#include "pw_assert/assert.h"
-#include "pw_rpc/internal/service.h"
-
-namespace pw::rpc::internal {
-
-// Manages a list of registered RPC services.
-class ServiceRegistry {
- public:
-  constexpr ServiceRegistry() : first_service_(nullptr) {}
-
-  ServiceRegistry(const ServiceRegistry& other) = delete;
-  ServiceRegistry& operator=(const ServiceRegistry& other) = delete;
-
-  void Register(Service& service) {
-    service.next_ = first_service_;
-    first_service_ = &service;
-  }
-
-  Service* Find(uint32_t id) const;
-
- private:
-  Service* first_service_;
-};
-
-}  // namespace pw::rpc::internal
diff --git a/pw_rpc/public/pw_rpc/server.h b/pw_rpc/public/pw_rpc/server.h
index 6a9809e..cf4c583 100644
--- a/pw_rpc/public/pw_rpc/server.h
+++ b/pw_rpc/public/pw_rpc/server.h
@@ -15,8 +15,9 @@
 
 #include <cstddef>
 
+#include "pw_containers/intrusive_list.h"
 #include "pw_rpc/channel.h"
-#include "pw_rpc/internal/service_registry.h"
+#include "pw_rpc/internal/service.h"
 
 namespace pw::rpc {
 namespace internal {
@@ -37,7 +38,7 @@
   // with an internal::Service; instead, use a generated class which inherits
   // from it.
   void RegisterService(internal::Service& service) {
-    services_.Register(service);
+    services_.push_front(service);
   }
 
   void ProcessPacket(span<const std::byte> packet, ChannelOutput& interface);
@@ -58,7 +59,7 @@
   Channel* AssignChannel(uint32_t id, ChannelOutput& interface);
 
   span<Channel> channels_;
-  internal::ServiceRegistry services_;
+  IntrusiveList<internal::Service> services_;
 };
 
 }  // namespace pw::rpc
diff --git a/pw_rpc/server.cc b/pw_rpc/server.cc
index b239f3d..bc12505 100644
--- a/pw_rpc/server.cc
+++ b/pw_rpc/server.cc
@@ -14,6 +14,8 @@
 
 #include "pw_rpc/server.h"
 
+#include <algorithm>
+
 #include "pw_log/log.h"
 #include "pw_rpc/internal/method.h"
 #include "pw_rpc/internal/packet.h"
@@ -71,8 +73,11 @@
                           Channel& channel,
                           internal::Packet& response,
                           span<std::byte> buffer) {
-  internal::Service* service = services_.Find(request.service_id());
-  if (service == nullptr) {
+  auto service = std::find_if(services_.begin(), services_.end(), [&](auto& s) {
+    return s.id() == request.service_id();
+  });
+
+  if (service == services_.end()) {
     // Couldn't find the requested service. Reply with a NOT_FOUND response
     // without the service_id field set.
     response.set_status(Status::NOT_FOUND);
diff --git a/pw_rpc/service_registry.cc b/pw_rpc/service_registry.cc
deleted file mode 100644
index d9182d7..0000000
--- a/pw_rpc/service_registry.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2020 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#include "pw_rpc/internal/service_registry.h"
-
-namespace pw::rpc::internal {
-
-Service* ServiceRegistry::Find(uint32_t id) const {
-  for (Service* s = first_service_; s != nullptr; s = s->next_) {
-    if (s->id() == id) {
-      return s;
-    }
-  }
-  return nullptr;
-}
-
-}  // namespace pw::rpc::internal
diff --git a/pw_rpc/test_impl/BUILD.gn b/pw_rpc/test_impl/BUILD.gn
index 3c050a8..68f88c1 100644
--- a/pw_rpc/test_impl/BUILD.gn
+++ b/pw_rpc/test_impl/BUILD.gn
@@ -25,6 +25,6 @@
 pw_source_set("test_impl") {
   public_configs = [ ":config" ]
   public = [ "public_overrides/pw_rpc/internal/method.h" ]
-  public_deps = [ "../:common" ]
+  public_deps = [ "../:server_library_deps" ]
   visibility = [ "..:*" ]
 }