Pigweed
 
Loading...
Searching...
No Matches
channel.h
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
28namespace pw::rpc {
29namespace internal {
30namespace test {
31
32template <typename, typename, uint32_t>
33class InvocationContext; // Forward declaration for friend statement
34
35} // namespace test
36
37class ChannelList; // Forward declaration for friend statement
38
39Status OverwriteChannelId(ByteSpan rpc_packet, uint32_t channel_id_under_128);
40
41} // namespace internal
42
45
57Result<uint32_t> ExtractChannelId(ConstByteSpan packet);
58
73template <uint32_t kNewChannelId>
74Status 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
84inline 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
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.
96constexpr 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
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.
112 constexpr ChannelOutput(const char* name) : name_(name) {}
113
114 virtual ~ChannelOutput() = default;
115
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.
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
149namespace internal {
150
151// Base class for rpc::Channel with internal-only public methods, which are
152// hidden in the public derived class.
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>
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>>
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>
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
205 constexpr uint32_t id() const { return id_; }
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
216 constexpr void Close() {
217 PW_ASSERT(id_ != kUnassignedChannelId);
218 id_ = kUnassignedChannelId;
219 output_ = nullptr;
220 }
221
222 protected:
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.
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>
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>>
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.
259 constexpr Channel() : internal::ChannelBase(kUnassignedChannelId, nullptr) {}
260
261 private:
262 template <typename, typename, uint32_t>
264 friend class internal::ChannelList;
265
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
Definition: status.h:85
Definition: channel.h:234
Definition: channel.h:104
Definition: channel.h:153
Status ChangeEncodedChannelId(ByteSpan rpc_packet)
Definition: channel.h:74
Result< uint32_t > ExtractChannelId(ConstByteSpan packet)