1 // Copyright 2021 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 <span> 17 18 #include "pw_containers/intrusive_list.h" 19 #include "pw_result/result.h" 20 #include "pw_rpc/internal/call.h" 21 #include "pw_rpc/internal/channel.h" 22 #include "pw_rpc/internal/channel_list.h" 23 #include "pw_rpc/internal/lock.h" 24 #include "pw_rpc/internal/packet.h" 25 #include "pw_sync/lock_annotations.h" 26 27 namespace pw::rpc::internal { 28 29 // Manages a list of channels and a list of ongoing calls for either a server or 30 // client. 31 // 32 // For clients, calls start when they send a REQUEST packet to a server. For 33 // servers, calls start when the REQUEST packet is received. In either case, 34 // calls add themselves to the Endpoint's list when they're started and 35 // remove themselves when they complete. Calls do this through their associated 36 // Server or Client object, which derive from Endpoint. 37 class Endpoint { 38 public: 39 ~Endpoint(); 40 41 // Creates a channel with the provided ID and ChannelOutput, if a channel slot 42 // is available or can be allocated (if PW_RPC_DYNAMIC_ALLOCATION is enabled). 43 // Returns: 44 // 45 // OK - the channel was opened successfully 46 // ALREADY_EXISTS - a channel with this ID is already present; remove it 47 // first 48 // RESOURCE_EXHAUSTED - no unassigned channels are available and 49 // PW_RPC_DYNAMIC_ALLOCATION is disabled 50 // OpenChannel(uint32_t id,ChannelOutput & interface)51 Status OpenChannel(uint32_t id, ChannelOutput& interface) 52 PW_LOCKS_EXCLUDED(rpc_lock()) { 53 LockGuard lock(rpc_lock()); 54 return channels_.Add(id, interface); 55 } 56 57 // Closes a channel and terminates any pending calls on that channel. 58 // If the calls are client requests, their on_error callback will be 59 // called with the ABORTED status. 60 Status CloseChannel(uint32_t channel_id) PW_LOCKS_EXCLUDED(rpc_lock()); 61 62 // For internal use only: returns the number calls in the RPC calls list. active_call_count()63 size_t active_call_count() const PW_LOCKS_EXCLUDED(rpc_lock()) { 64 LockGuard lock(rpc_lock()); 65 return calls_.size(); 66 } 67 68 // For internal use only: finds an internal::Channel with this ID or nullptr 69 // if none matches. GetInternalChannel(uint32_t channel_id)70 Channel* GetInternalChannel(uint32_t channel_id) 71 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 72 return channels_.Get(channel_id); 73 } 74 75 protected: Endpoint(std::span<rpc::Channel> channels)76 _PW_RPC_CONSTEXPR Endpoint(std::span<rpc::Channel> channels) 77 : channels_(std::span(static_cast<internal::Channel*>(channels.data()), 78 channels.size())), 79 next_call_id_(0) {} 80 81 // Parses an RPC packet and sets ongoing_call to the matching call, if any. 82 // Returns the parsed packet or an error. 83 Result<Packet> ProcessPacket(std::span<const std::byte> data, 84 Packet::Destination destination) 85 PW_LOCKS_EXCLUDED(rpc_lock()); 86 87 // Finds a call object for an ongoing call associated with this packet, if 88 // any. Returns nullptr if no matching call exists. FindCall(const Packet & packet)89 Call* FindCall(const Packet& packet) PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 90 return FindCallById( 91 packet.channel_id(), packet.service_id(), packet.method_id()); 92 } 93 94 private: 95 // Give Call access to the register/unregister functions. 96 friend class Call; 97 98 // Returns an ID that can be assigned to a new call. NewCallId()99 uint32_t NewCallId() PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 100 // Call IDs are varint encoded. Limit the varint size to 2 bytes (14 usable 101 // bits). 102 constexpr uint32_t kMaxCallId = 1 << 14; 103 return (++next_call_id_) % kMaxCallId; 104 } 105 106 // Adds a call to the internal call registry. If a matching call already 107 // exists, it is cancelled locally (on_error called, no packet sent). 108 void RegisterCall(Call& call) PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()); 109 110 // Registers a call that is known to be unique. The calls list is NOT checked 111 // for existing calls. RegisterUniqueCall(Call & call)112 void RegisterUniqueCall(Call& call) PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 113 calls_.push_front(call); 114 } 115 116 // Removes the provided call from the call registry. UnregisterCall(const Call & call)117 void UnregisterCall(const Call& call) 118 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) { 119 calls_.remove(call); 120 } 121 122 Call* FindCallById(uint32_t channel_id, 123 uint32_t service_id, 124 uint32_t method_id) 125 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()); 126 127 ChannelList channels_ PW_GUARDED_BY(rpc_lock()); 128 IntrusiveList<Call> calls_ PW_GUARDED_BY(rpc_lock()); 129 130 uint32_t next_call_id_ PW_GUARDED_BY(rpc_lock()); 131 }; 132 133 } // namespace pw::rpc::internal 134