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