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