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