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