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