• 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/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 
103   // Give call classes access to OpenCall.
104   friend class RawServerReaderWriter;
105   friend class RawServerWriter;
106   friend class RawServerReader;
107   friend class RawUnaryResponder;
108 
109   template <typename, typename>
110   friend class NanopbServerReaderWriter;
111   template <typename>
112   friend class NanopbServerWriter;
113   template <typename, typename>
114   friend class NanopbServerReader;
115   template <typename>
116   friend class NanopbUnaryResponder;
117 
118   template <typename, typename>
119   friend class PwpbServerReaderWriter;
120   template <typename>
121   friend class PwpbServerWriter;
122   template <typename, typename>
123   friend class PwpbServerReader;
124   template <typename>
125   friend class PwpbUnaryResponder;
126 
127   // Opens a call object for an unrequested RPC. Calls created with OpenCall
128   // use a special call ID and will adopt the call ID from the first packet for
129   // their channel, service, and method. Only one call object may be opened in
130   // this fashion at a time.
131   //
132   // This function checks the type of RPC at compile time.
133   template <typename CallType,
134             auto kMethod,
135             MethodType kExpected,
136             typename ServiceImpl,
137             typename MethodImpl>
OpenCall(uint32_t channel_id,ServiceImpl & service,const MethodImpl & method)138   [[nodiscard]] CallType OpenCall(uint32_t channel_id,
139                                   ServiceImpl& service,
140                                   const MethodImpl& method)
141       PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
142     internal::rpc_lock().lock();
143 
144     using Info = internal::MethodInfo<kMethod>;
145     if constexpr (kExpected == MethodType::kUnary) {
146       static_assert(
147           Info::kType == kExpected,
148           "UnaryResponder objects may only be opened for unary RPCs.");
149     } else if constexpr (kExpected == MethodType::kServerStreaming) {
150       static_assert(
151           Info::kType == kExpected,
152           "ServerWriters may only be opened for server streaming RPCs.");
153     } else if constexpr (kExpected == MethodType::kClientStreaming) {
154       static_assert(
155           Info::kType == kExpected,
156           "ServerReaders may only be opened for client streaming RPCs.");
157     } else if constexpr (kExpected == MethodType::kBidirectionalStreaming) {
158       static_assert(Info::kType == kExpected,
159                     "ServerReaderWriters may only be opened for bidirectional "
160                     "streaming RPCs.");
161     }
162 
163     CallType call(internal::CallContext(
164                       *this, channel_id, service, method, internal::kOpenCallId)
165                       .ClaimLocked());
166     CleanUpCalls();
167     return call;
168   }
169 
170   std::tuple<Service*, const internal::Method*> FindMethod(
171       const internal::Packet& packet)
172       PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock());
173 
174   void HandleClientStreamPacket(const internal::Packet& packet,
175                                 internal::Channel& channel,
176                                 IntrusiveList<internal::Call>::iterator call)
177       const PW_UNLOCK_FUNCTION(internal::rpc_lock());
178 
179   template <typename... OtherServices>
UnregisterServiceLocked(Service & service,OtherServices &...services)180   void UnregisterServiceLocked(Service& service, OtherServices&... services)
181       PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()) {
182     services_.remove(service);
183     UnregisterServiceLocked(services...);
184     AbortCallsForService(service);
185   }
186 
UnregisterServiceLocked()187   void UnregisterServiceLocked() {}  // Base case; nothing left to do.
188 
189   // Remove these internal::Endpoint functions from the public interface.
190   using Endpoint::active_call_count;
191   using Endpoint::ClaimLocked;
192   using Endpoint::CleanUpCalls;
193   using Endpoint::GetInternalChannel;
194 
195   IntrusiveList<Service> services_ PW_GUARDED_BY(internal::rpc_lock());
196 };
197 
198 }  // namespace pw::rpc
199