C/C++ API Reference
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
45
51
59template <uint32_t kNewChannelId>
61 static_assert(kNewChannelId < 128u,
62 "Channel IDs must be less than 128 to avoid needing to "
63 "re-encode the packet");
64 return internal::OverwriteChannelId(rpc_packet, kNewChannelId);
65}
66
71 uint32_t new_channel_id) {
72 PW_ASSERT(new_channel_id < 128);
73 return internal::OverwriteChannelId(rpc_packet, new_channel_id);
74}
75
88constexpr size_t MaxSafePayloadSize(
89 size_t encode_buffer_size = cfg::kEncodingBufferSizeBytes) {
90 return encode_buffer_size > internal::Packet::kMinEncodedSizeWithoutPayload
91 ? encode_buffer_size -
92 internal::Packet::kMinEncodedSizeWithoutPayload
93 : 0;
94}
95
97 public:
98 // Returned from MaximumTransmissionUnit() to indicate that this ChannelOutput
99 // imposes no limits on the MTU.
100 static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max();
101
102 // Creates a channel output with the provided name. The name is used for
103 // logging only.
104 constexpr ChannelOutput(const char* name) : name_(name) {}
105
106 virtual ~ChannelOutput() = default;
107
108 constexpr const char* name() const { return name_; }
109
110 // Returns the maximum transmission unit that this ChannelOutput supports. If
111 // the ChannelOutput imposes no limit on the MTU, this function returns
112 // ChannelOutput::kUnlimited.
113 virtual size_t MaximumTransmissionUnit() { return kUnlimited; }
114
115#if PW_RPC_LOCKLESS_CHANNEL_SEND
116#define PW_RPC_CHANNEL_OUTPUT_SEND_LOCK_REQUIREMENT
117#else
118#define PW_RPC_CHANNEL_OUTPUT_SEND_LOCK_REQUIREMENT \
119 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock())
120#endif
121
122 // Sends an encoded RPC packet. Returns OK if further packets may be sent,
123 // even if the current packet could not be sent. Returns any other status if
124 // the Channel is no longer able to send packets.
125 //
126 // By default, the RPC system’s internal lock is held while this function is
127 // called. Avoid long-running operations, since these will delay any other
128 // users of the RPC system.
129 //
130 // If PW_RPC_LOCKLESS_CHANNEL_SEND is enabled, the global lock is released
131 // before calling Send(). In this case, implementations should still avoid
132 // long-running operations as they will block channel modifications (adding,
133 // moving, or deleting channels).
134 //
135 // !!! DANGER !!!
136 //
137 // No pw_rpc APIs may be accessed in this function! Implementations MUST NOT
138 // access any RPC endpoints (pw::rpc::Client, pw::rpc::Server) or call objects
139 // (pw::rpc::ServerReaderWriter, pw::rpc::ClientReaderWriter, etc.) inside the
140 // Send() function or any descendent calls.
141 //
142 // When `PW_RPC_LOCKLESS_CHANNEL_SEND` is disabled, doing so will result in
143 // deadlock. Even when enabled, this is still a good rule to follow.
144 // RPC APIs may be used by other threads, just not within Send().
145 //
146 // The buffer provided in packet must NOT be accessed outside of this
147 // function. It must be sent immediately or copied elsewhere before the
148 // function returns.
149 virtual Status Send(span<const std::byte> buffer)
150 PW_RPC_CHANNEL_OUTPUT_SEND_LOCK_REQUIREMENT = 0;
151
152 private:
153 const char* name_;
154};
155
157
158namespace internal {
159
160// Base class for rpc::Channel with internal-only public methods, which are
161// hidden in the public derived class.
163 public:
164 static constexpr uint32_t kUnassignedChannelId = 0;
165
166 // TODO: b/234876441 - Remove the Configure and set_channel_output functions.
167 // Users should call CloseChannel() / OpenChannel() to change a channel.
168 // This ensures calls are properly update and works consistently between
169 // static and dynamic channel allocation.
170
171 // Manually configures a dynamically-assignable channel with a specified ID
172 // and output. This is useful when a channel's parameters are not known until
173 // runtime. This can only be called once per channel.
174 template <typename UnusedType = void>
175 constexpr void Configure(uint32_t id, ChannelOutput& output) {
176 static_assert(
177 !cfg::kDynamicAllocationEnabled<UnusedType>,
178 "Configure() may not be used if PW_RPC_DYNAMIC_ALLOCATION is "
179 "enabled. Call CloseChannel/OpenChannel on the endpoint instead.");
180 PW_ASSERT(id_ == kUnassignedChannelId);
181 PW_ASSERT(id != kUnassignedChannelId);
182 id_ = id;
183 output_ = &output;
184 }
185
186 // Configure using an enum value channel ID.
187 template <typename T,
188 typename = std::enable_if_t<std::is_enum_v<T>>,
189 typename U = std::underlying_type_t<T>>
190 constexpr void Configure(T id, ChannelOutput& output) {
191 static_assert(
192 !cfg::kDynamicAllocationEnabled<T>,
193 "Configure() may not be used if PW_RPC_DYNAMIC_ALLOCATION is enabled. "
194 "Call CloseChannel/OpenChannel on the endpoint instead.");
195 static_assert(sizeof(U) <= sizeof(uint32_t));
196 const U kIntId = static_cast<U>(id);
197 PW_ASSERT(kIntId > 0);
198 return Configure<T>(static_cast<uint32_t>(kIntId), output);
199 }
200
201 // Reconfigures a channel with a new output. Depending on the output's
202 // implementatation, there might be unintended behavior if the output is in
203 // use.
204 template <typename UnusedType = void>
205 constexpr void set_channel_output(ChannelOutput& output) {
206 static_assert(
207 !cfg::kDynamicAllocationEnabled<UnusedType>,
208 "set_channel_output() may not be used if PW_RPC_DYNAMIC_ALLOCATION is "
209 "enabled. Call CloseChannel/OpenChannel on the endpoint instead.");
210 PW_ASSERT(id_ != kUnassignedChannelId);
211 output_ = &output;
212 }
213
214 constexpr uint32_t id() const { return id_; }
215 constexpr bool assigned() const { return id_ != kUnassignedChannelId; }
216
217 //
218 // Internal functions made private in the public Channel class.
219 //
220
221 // Invokes ChannelOutput::Send and returns its status. Any non-OK status
222 // indicates that the Channel is permanently closed.
223 Status Send(const Packet& packet) PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock());
224
225 constexpr void Close() {
226 PW_ASSERT(id_ != kUnassignedChannelId);
227 id_ = kUnassignedChannelId;
228 output_ = nullptr;
229 }
230
231 // Returns the maximum payload size for this channel, factoring in the
232 // ChannelOutput's MTU and the RPC system's `pw::rpc::MaxSafePayloadSize()`.
233 size_t MaxWriteSizeBytes() const;
234
235 protected:
236 constexpr ChannelBase(uint32_t id, ChannelOutput* output)
237 : id_(id), output_(output) {}
238
239 private:
240 uint32_t id_;
241 ChannelOutput* output_;
242};
243
244} // namespace internal
245
246// Associates an ID with an interface for sending packets.
248 public:
249 // Creates a channel with a static ID. The channel's output can also be
250 // static, or it can set to null to allow dynamically opening connections
251 // through the channel.
252 template <uint32_t kId>
253 constexpr static Channel Create(ChannelOutput* output) {
254 static_assert(kId != kUnassignedChannelId, "Channel ID cannot be 0");
255 return Channel(kId, output);
256 }
257
258 // Creates a channel with a static ID from an enum value.
259 template <auto kId,
260 typename T = decltype(kId),
261 typename = std::enable_if_t<std::is_enum_v<T>>,
262 typename U = std::underlying_type_t<T>>
263 constexpr static Channel Create(ChannelOutput* output) {
264 constexpr U kIntId = static_cast<U>(kId);
265 static_assert(kIntId >= 0, "Channel ID cannot be negative");
266 static_assert(kIntId <= std::numeric_limits<uint32_t>::max(),
267 "Channel ID must fit in a uint32");
268 return Create<static_cast<uint32_t>(kIntId)>(output);
269 }
270
271 // Creates a dynamically assignable channel without a set ID or output.
272 constexpr Channel() : internal::ChannelBase(kUnassignedChannelId, nullptr) {}
273
274 private:
275 template <typename, typename, uint32_t>
277 friend class internal::ChannelList;
278
279 constexpr Channel(uint32_t id, ChannelOutput* output)
280 : internal::ChannelBase(id, output) {
281 PW_ASSERT(id != kUnassignedChannelId);
282 }
283
284 private:
285 // Hide internal-only methods defined in the internal::ChannelBase.
286 using internal::ChannelBase::Close;
287 using internal::ChannelBase::MaxWriteSizeBytes;
288 using internal::ChannelBase::Send;
289};
290
291} // namespace pw::rpc
Definition: result.h:145
Definition: status.h:120
Definition: channel.h:247
Definition: channel.h:96
Definition: channel.h:162
constexpr size_t MaxSafePayloadSize(size_t encode_buffer_size=cfg::kEncodingBufferSizeBytes)
Definition: channel.h:88
Status ChangeEncodedChannelId(ByteSpan rpc_packet)
Definition: channel.h:60
Result< uint32_t > ExtractChannelId(ConstByteSpan packet)
#define PW_EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: lock_annotations.h:147