xref: /aosp_15_r20/external/pigweed/pw_rpc/public/pw_rpc/server.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <cstddef>
17 #include <tuple>
18 
19 #include "pw_containers/intrusive_list.h"
20 #include "pw_rpc/channel.h"
21 #include "pw_rpc/internal/call.h"
22 #include "pw_rpc/internal/endpoint.h"
23 #include "pw_rpc/internal/grpc.h"
24 #include "pw_rpc/internal/lock.h"
25 #include "pw_rpc/internal/method.h"
26 #include "pw_rpc/internal/method_info.h"
27 #include "pw_rpc/internal/server_call.h"
28 #include "pw_rpc/service.h"
29 #include "pw_span/span.h"
30 #include "pw_status/status.h"
31 
32 namespace pw::rpc {
33 
34 class Server : public internal::Endpoint {
35  public:
36   // If dynamic allocation is supported, it is not necessary to preallocate a
37   // channels list.
38 #if PW_RPC_DYNAMIC_ALLOCATION
39   _PW_RPC_CONSTEXPR Server() = default;
40 #endif  // PW_RPC_DYNAMIC_ALLOCATION
41 
42   // Creates a client that uses a set of RPC channels. Channels can be shared
43   // between multiple clients and servers.
Server(span<Channel> channels)44   _PW_RPC_CONSTEXPR Server(span<Channel> channels) : Endpoint(channels) {}
45 
46   // Registers one or more services with the server. This should not be called
47   // directly with a Service; instead, use a generated class which inherits
48   // from it.
49   //
50   // This function may be called with any number of services. Combining
51   // registration into fewer calls is preferred so the RPC mutex is only
52   // locked/unlocked once.
53   template <typename... OtherServices>
RegisterService(Service & service,OtherServices &...services)54   void RegisterService(Service& service, OtherServices&... services)
55       PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
56     internal::RpcLockGuard lock;
57     services_.push_front(service);  // Register the first service
58 
59     // Register any additional services by expanding the parameter pack. This
60     // is a fold expression of the comma operator.
61     (services_.push_front(services), ...);
62   }
63 
64   // Returns whether a service is registered.
65   //
66   // Calling RegisterService with a registered service will assert. So depending
67   // on your logic you might want to check if a service is currently registered.
IsServiceRegistered(const Service & service)68   bool IsServiceRegistered(const Service& service) const
69       PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
70     internal::RpcLockGuard lock;
71 
72     for (const Service& svc : services_) {
73       if (&svc == &service) {
74         return true;
75       }
76     }
77 
78     return false;
79   }
80 
81   template <typename... OtherServices>
UnregisterService(Service & service,OtherServices &...services)82   void UnregisterService(Service& service, OtherServices&... services)
83       PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
84     internal::rpc_lock().lock();
85     UnregisterServiceLocked(service, static_cast<Service&>(services)...);
86     CleanUpCalls();
87   }
88 
89   // Processes an RPC packet. The packet may contain an RPC request or a control
90   // packet, the result of which is processed in this function. Returns whether
91   // the packet was able to be processed:
92   //
93   //   OK - The packet was processed by the server.
94   //   DATA_LOSS - Failed to decode the packet.
95   //   INVALID_ARGUMENT - The packet is intended for a client, not a server.
96   //   UNAVAILABLE - No RPC channel with the requested ID was found.
97   Status ProcessPacket(ConstByteSpan packet_data)
98       PW_LOCKS_EXCLUDED(internal::rpc_lock());
99 
100  private:
101   friend class internal::Call;
102   friend class ServerTestHelper;
103 
104   // Give gRPC integration access to FindMethod and internal::Packet version of
105   // ProcessPacket
106   friend class pw::grpc::PwRpcHandler;
107 
108   // Give call classes access to OpenCall.
109   friend class RawServerReaderWriter;
110   friend class RawServerWriter;
111   friend class RawServerReader;
112   friend class RawUnaryResponder;
113 
114   template <typename, typename>
115   friend class NanopbServerReaderWriter;
116   template <typename>
117   friend class NanopbServerWriter;
118   template <typename, typename>
119   friend class NanopbServerReader;
120   template <typename>
121   friend class NanopbUnaryResponder;
122 
123   template <typename, typename>
124   friend class PwpbServerReaderWriter;
125   template <typename>
126   friend class PwpbServerWriter;
127   template <typename, typename>
128   friend class PwpbServerReader;
129   template <typename>
130   friend class PwpbUnaryResponder;
131 
132   // Opens a call object for an unrequested RPC. Calls created with OpenCall
133   // use a special call ID and will adopt the call ID from the first packet for
134   // their channel, service, and method. Only one call object may be opened in
135   // this fashion at a time.
136   //
137   // This function checks the type of RPC at compile time.
138   template <typename CallType,
139             auto kMethod,
140             MethodType kExpected,
141             typename ServiceImpl,
142             typename MethodImpl>
OpenCall(uint32_t channel_id,ServiceImpl & service,const MethodImpl & method)143   [[nodiscard]] CallType OpenCall(uint32_t channel_id,
144                                   ServiceImpl& service,
145                                   const MethodImpl& method)
146       PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
147     internal::rpc_lock().lock();
148 
149     using Info = internal::MethodInfo<kMethod>;
150     if constexpr (kExpected == MethodType::kUnary) {
151       static_assert(
152           Info::kType == kExpected,
153           "UnaryResponder objects may only be opened for unary RPCs.");
154     } else if constexpr (kExpected == MethodType::kServerStreaming) {
155       static_assert(
156           Info::kType == kExpected,
157           "ServerWriters may only be opened for server streaming RPCs.");
158     } else if constexpr (kExpected == MethodType::kClientStreaming) {
159       static_assert(
160           Info::kType == kExpected,
161           "ServerReaders may only be opened for client streaming RPCs.");
162     } else if constexpr (kExpected == MethodType::kBidirectionalStreaming) {
163       static_assert(Info::kType == kExpected,
164                     "ServerReaderWriters may only be opened for bidirectional "
165                     "streaming RPCs.");
166     }
167 
168     CallType call(internal::CallContext(
169                       *this, channel_id, service, method, internal::kOpenCallId)
170                       .ClaimLocked());
171     CleanUpCalls();
172     return call;
173   }
174 
175   std::tuple<Service*, const internal::Method*> FindMethod(uint32_t service_id,
176                                                            uint32_t method_id)
177       PW_LOCKS_EXCLUDED(internal::rpc_lock());
178 
179   std::tuple<Service*, const internal::Method*> FindMethodLocked(
180       uint32_t service_id, uint32_t method_id)
181       PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock());
182 
FindMethodLocked(const internal::Packet & packet)183   std::tuple<Service*, const internal::Method*> FindMethodLocked(
184       const internal::Packet& packet)
185       PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()) {
186     // Packets always include service and method IDs.
187     return FindMethodLocked(packet.service_id(), packet.method_id());
188   }
189 
190   void HandleCompletionRequest(const internal::Packet& packet,
191                                internal::ChannelBase& channel,
192                                IntrusiveList<internal::Call>::iterator call)
193       const PW_UNLOCK_FUNCTION(internal::rpc_lock());
194 
195   void HandleClientStreamPacket(const internal::Packet& packet,
196                                 internal::ChannelBase& channel,
197                                 IntrusiveList<internal::Call>::iterator call)
198       const PW_UNLOCK_FUNCTION(internal::rpc_lock());
199 
200   template <typename... OtherServices>
UnregisterServiceLocked(Service & service,OtherServices &...services)201   void UnregisterServiceLocked(Service& service, OtherServices&... services)
202       PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()) {
203     services_.remove(service);
204     UnregisterServiceLocked(services...);
205     AbortCallsForService(service);
206   }
207 
UnregisterServiceLocked()208   void UnregisterServiceLocked() {}  // Base case; nothing left to do.
209 
210   Status ProcessPacket(internal::Packet packet)
211       PW_LOCKS_EXCLUDED(internal::rpc_lock());
212 
213   // Remove these internal::Endpoint functions from the public interface.
214   using Endpoint::active_call_count;
215   using Endpoint::ClaimLocked;
216   using Endpoint::CleanUpCalls;
217   using Endpoint::GetInternalChannel;
218 
219   IntrusiveList<Service> services_ PW_GUARDED_BY(internal::rpc_lock());
220 };
221 
222 }  // namespace pw::rpc
223