• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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