xref: /aosp_15_r20/external/pigweed/pw_rpc/public/pw_rpc/channel.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 <cstdint>
17 #include <limits>
18 #include <type_traits>
19 
20 #include "pw_assert/assert.h"
21 #include "pw_bytes/span.h"
22 #include "pw_result/result.h"
23 #include "pw_rpc/internal/lock.h"
24 #include "pw_rpc/internal/packet.h"
25 #include "pw_span/span.h"
26 #include "pw_status/status.h"
27 
28 namespace pw::rpc {
29 namespace internal {
30 namespace test {
31 
32 template <typename, typename, uint32_t>
33 class InvocationContext;  // Forward declaration for friend statement
34 
35 }  // namespace test
36 
37 class ChannelList;  // Forward declaration for friend statement
38 
39 Status OverwriteChannelId(ByteSpan rpc_packet, uint32_t channel_id_under_128);
40 
41 }  // namespace internal
42 
43 /// @defgroup pw_rpc_channel_functions
44 /// @{
45 
46 /// Extracts the channel ID from a pw_rpc packet.
47 ///
48 /// @returns @rst
49 ///
50 /// .. pw-status-codes::
51 ///
52 ///    OK: returns the channel ID in the packet
53 ///
54 ///    DATA_LOSS: the packet is corrupt and the channel ID could not be found.
55 ///
56 /// @endrst
57 Result<uint32_t> ExtractChannelId(ConstByteSpan packet);
58 
59 /// Rewrites an encoded packet's channel ID in place. Both channel IDs MUST be
60 /// less than 128.
61 ///
62 /// @returns @rst
63 ///
64 /// .. pw-status-codes::
65 ///
66 ///    OK: Successfully replaced the channel ID
67 ///
68 ///    DATA_LOSS: parsing the packet failed
69 ///
70 ///    OUT_OF_RANGE: the encoded packet's channel ID was 128 or larger
71 ///
72 /// @endrst
73 template <uint32_t kNewChannelId>
ChangeEncodedChannelId(ByteSpan rpc_packet)74 Status ChangeEncodedChannelId(ByteSpan rpc_packet) {
75   static_assert(kNewChannelId < 128u,
76                 "Channel IDs must be less than 128 to avoid needing to "
77                 "re-encode the packet");
78   return internal::OverwriteChannelId(rpc_packet, kNewChannelId);
79 }
80 
81 /// Version of `ChangeEncodedChannelId` with a runtime variable channel ID.
82 /// Prefer the template parameter version when possible to avoid a runtime check
83 /// on the new channel ID.
ChangeEncodedChannelId(ByteSpan rpc_packet,uint32_t new_channel_id)84 inline Status ChangeEncodedChannelId(ByteSpan rpc_packet,
85                                      uint32_t new_channel_id) {
86   PW_ASSERT(new_channel_id < 128);
87   return internal::OverwriteChannelId(rpc_packet, new_channel_id);
88 }
89 
90 /// @}
91 
92 // Returns the maximum size of the payload of an RPC packet. This can be used
93 // when allocating response encode buffers for RPC services.
94 // If the RPC encode buffer is too small to fit RPC packet headers, this will
95 // return zero.
96 constexpr size_t MaxSafePayloadSize(
97     size_t encode_buffer_size = cfg::kEncodingBufferSizeBytes) {
98   return encode_buffer_size > internal::Packet::kMinEncodedSizeWithoutPayload
99              ? encode_buffer_size -
100                    internal::Packet::kMinEncodedSizeWithoutPayload
101              : 0;
102 }
103 
104 class ChannelOutput {
105  public:
106   // Returned from MaximumTransmissionUnit() to indicate that this ChannelOutput
107   // imposes no limits on the MTU.
108   static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max();
109 
110   // Creates a channel output with the provided name. The name is used for
111   // logging only.
ChannelOutput(const char * name)112   constexpr ChannelOutput(const char* name) : name_(name) {}
113 
114   virtual ~ChannelOutput() = default;
115 
name()116   constexpr const char* name() const { return name_; }
117 
118   // Returns the maximum transmission unit that this ChannelOutput supports. If
119   // the ChannelOutput imposes no limit on the MTU, this function returns
120   // ChannelOutput::kUnlimited.
MaximumTransmissionUnit()121   virtual size_t MaximumTransmissionUnit() { return kUnlimited; }
122 
123   // Sends an encoded RPC packet. Returns OK if further packets may be sent,
124   // even if the current packet could not be sent. Returns any other status if
125   // the Channel is no longer able to send packets.
126   //
127   // The RPC system’s internal lock is held while this function is called. Avoid
128   // long-running operations, since these will delay any other users of the RPC
129   // system.
130   //
131   // !!! DANGER !!!
132   //
133   // No pw_rpc APIs may be accessed in this function! Implementations MUST NOT
134   // access any RPC endpoints (pw::rpc::Client, pw::rpc::Server) or call objects
135   // (pw::rpc::ServerReaderWriter, pw::rpc::ClientReaderWriter, etc.) inside the
136   // Send() function or any descendent calls. Doing so will result in deadlock!
137   // RPC APIs may be used by other threads, just not within Send().
138   //
139   // The buffer provided in packet must NOT be accessed outside of this
140   // function. It must be sent immediately or copied elsewhere before the
141   // function returns.
142   virtual Status Send(span<const std::byte> buffer)
143       PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()) = 0;
144 
145  private:
146   const char* name_;
147 };
148 
149 namespace internal {
150 
151 // Base class for rpc::Channel with internal-only public methods, which are
152 // hidden in the public derived class.
153 class ChannelBase {
154  public:
155   static constexpr uint32_t kUnassignedChannelId = 0;
156 
157   // TODO: b/234876441 - Remove the Configure and set_channel_output functions.
158   //     Users should call CloseChannel() / OpenChannel() to change a channel.
159   //     This ensures calls are properly update and works consistently between
160   //     static and dynamic channel allocation.
161 
162   // Manually configures a dynamically-assignable channel with a specified ID
163   // and output. This is useful when a channel's parameters are not known until
164   // runtime. This can only be called once per channel.
165   template <typename UnusedType = void>
Configure(uint32_t id,ChannelOutput & output)166   constexpr void Configure(uint32_t id, ChannelOutput& output) {
167     static_assert(
168         !cfg::kDynamicAllocationEnabled<UnusedType>,
169         "Configure() may not be used if PW_RPC_DYNAMIC_ALLOCATION is "
170         "enabled. Call CloseChannel/OpenChannel on the endpoint instead.");
171     PW_ASSERT(id_ == kUnassignedChannelId);
172     PW_ASSERT(id != kUnassignedChannelId);
173     id_ = id;
174     output_ = &output;
175   }
176 
177   // Configure using an enum value channel ID.
178   template <typename T,
179             typename = std::enable_if_t<std::is_enum_v<T>>,
180             typename U = std::underlying_type_t<T>>
Configure(T id,ChannelOutput & output)181   constexpr void Configure(T id, ChannelOutput& output) {
182     static_assert(
183         !cfg::kDynamicAllocationEnabled<T>,
184         "Configure() may not be used if PW_RPC_DYNAMIC_ALLOCATION is enabled. "
185         "Call CloseChannel/OpenChannel on the endpoint instead.");
186     static_assert(sizeof(U) <= sizeof(uint32_t));
187     const U kIntId = static_cast<U>(id);
188     PW_ASSERT(kIntId > 0);
189     return Configure<T>(static_cast<uint32_t>(kIntId), output);
190   }
191 
192   // Reconfigures a channel with a new output. Depending on the output's
193   // implementatation, there might be unintended behavior if the output is in
194   // use.
195   template <typename UnusedType = void>
set_channel_output(ChannelOutput & output)196   constexpr void set_channel_output(ChannelOutput& output) {
197     static_assert(
198         !cfg::kDynamicAllocationEnabled<UnusedType>,
199         "set_channel_output() may not be used if PW_RPC_DYNAMIC_ALLOCATION is "
200         "enabled. Call CloseChannel/OpenChannel on the endpoint instead.");
201     PW_ASSERT(id_ != kUnassignedChannelId);
202     output_ = &output;
203   }
204 
id()205   constexpr uint32_t id() const { return id_; }
assigned()206   constexpr bool assigned() const { return id_ != kUnassignedChannelId; }
207 
208   //
209   // Internal functions made private in the public Channel class.
210   //
211 
212   // Invokes ChannelOutput::Send and returns its status. Any non-OK status
213   // indicates that the Channel is permanently closed.
214   Status Send(const Packet& packet) PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock());
215 
Close()216   constexpr void Close() {
217     PW_ASSERT(id_ != kUnassignedChannelId);
218     id_ = kUnassignedChannelId;
219     output_ = nullptr;
220   }
221 
222  protected:
ChannelBase(uint32_t id,ChannelOutput * output)223   constexpr ChannelBase(uint32_t id, ChannelOutput* output)
224       : id_(id), output_(output) {}
225 
226  private:
227   uint32_t id_;
228   ChannelOutput* output_;
229 };
230 
231 }  // namespace internal
232 
233 // Associates an ID with an interface for sending packets.
234 class Channel : public internal::ChannelBase {
235  public:
236   // Creates a channel with a static ID. The channel's output can also be
237   // static, or it can set to null to allow dynamically opening connections
238   // through the channel.
239   template <uint32_t kId>
Create(ChannelOutput * output)240   constexpr static Channel Create(ChannelOutput* output) {
241     static_assert(kId != kUnassignedChannelId, "Channel ID cannot be 0");
242     return Channel(kId, output);
243   }
244 
245   // Creates a channel with a static ID from an enum value.
246   template <auto kId,
247             typename T = decltype(kId),
248             typename = std::enable_if_t<std::is_enum_v<T>>,
249             typename U = std::underlying_type_t<T>>
Create(ChannelOutput * output)250   constexpr static Channel Create(ChannelOutput* output) {
251     constexpr U kIntId = static_cast<U>(kId);
252     static_assert(kIntId >= 0, "Channel ID cannot be negative");
253     static_assert(kIntId <= std::numeric_limits<uint32_t>::max(),
254                   "Channel ID must fit in a uint32");
255     return Create<static_cast<uint32_t>(kIntId)>(output);
256   }
257 
258   // Creates a dynamically assignable channel without a set ID or output.
Channel()259   constexpr Channel() : internal::ChannelBase(kUnassignedChannelId, nullptr) {}
260 
261  private:
262   template <typename, typename, uint32_t>
263   friend class internal::test::InvocationContext;
264   friend class internal::ChannelList;
265 
Channel(uint32_t id,ChannelOutput * output)266   constexpr Channel(uint32_t id, ChannelOutput* output)
267       : internal::ChannelBase(id, output) {
268     PW_ASSERT(id != kUnassignedChannelId);
269   }
270 
271  private:
272   // Hide internal-only methods defined in the internal::ChannelBase.
273   using internal::ChannelBase::Close;
274   using internal::ChannelBase::Send;
275 };
276 
277 }  // namespace pw::rpc
278