C/C++ API Reference
Loading...
Searching...
No Matches
packet_channel.h
1// Copyright 2025 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// __ ___ ___ _ _ ___ _ _ ___
17// \ \ / /_\ | _ \ \| |_ _| \| |/ __|
18// \ \/\/ / _ \| / .` || || .` | (_ |
19// \_/\_/_/ \_\_|_\_|\_|___|_|\_|\___|
20// _____ _____ ___ ___ ___ __ __ ___ _ _ _____ _ _
21// | __\ \/ / _ \ __| _ \_ _| \/ | __| \| |_ _/_\ | |
22// | _| > <| _/ _|| /| || |\/| | _|| .` | | |/ _ \| |__
23// |___/_/\_\_| |___|_|_\___|_| |_|___|_|\_| |_/_/ \_\____|
24//
25// This module is in an early, experimental state. DO NOT USE THIS CLASS until
26// this banner has been removed.
27
28#include <cstddef>
29#include <utility>
30
31#include "lib/stdcompat/utility.h"
32#include "pw_assert/assert.h"
33#include "pw_async2/context.h"
34#include "pw_async2/poll.h"
35#include "pw_async2/try.h"
36#include "pw_async2/waker.h"
37#include "pw_channel/properties.h"
38#include "pw_result/result.h"
39#include "pw_status/status.h"
40
41namespace pw::channel {
42
44
47template <typename Packet>
49 public:
50 constexpr PendingWrite() : channel_(nullptr), num_packets_(0) {}
51
52 PendingWrite(const PendingWrite&) = delete;
53 PendingWrite& operator=(const PendingWrite&) = delete;
54
55 constexpr PendingWrite(PendingWrite&& other)
56 : channel_(other.channel_),
57 num_packets_(cpp20::exchange(other.num_packets_, 0u)) {}
58 constexpr PendingWrite& operator=(PendingWrite&& other) {
59 channel_ = other.channel_;
60 num_packets_ = cpp20::exchange(other.num_packets_, 0u);
61 return *this;
62 }
63
65 // TODO: b/421961717 - Consider allowing staged writes to be discarded
66 PW_ASSERT(num_packets_ == 0u);
67 }
68
73 void Stage(Packet&& packet) {
74 PW_ASSERT(num_packets_ > 0u);
75 channel_->DoStageWrite(std::move(packet));
76 num_packets_ -= 1;
77 }
78
79 size_t num_packets() const { return num_packets_; }
80
81 private:
82 friend class AnyPacketChannel<Packet>;
83
84 constexpr PendingWrite(AnyPacketChannel<Packet>& channel, size_t num_packets)
85 : channel_(&channel), num_packets_(num_packets) {}
86
87 AnyPacketChannel<Packet>* channel_;
88 size_t num_packets_;
89};
90
93
96inline constexpr uint16_t kNoFlowControl = std::numeric_limits<uint16_t>::max();
97
110template <typename T, Property... kProperties>
112 public:
113 using Packet = T;
114
115 // @copydoc AnyPacketChannel::readable
116 [[nodiscard]] static constexpr bool readable() {
117 return ((kProperties == Property::kReadable) || ...);
118 }
119 // @copydoc AnyPacketChannel::writable
120 [[nodiscard]] static constexpr bool writable() {
121 return ((kProperties == Property::kWritable) || ...);
122 }
123
124 // @copydoc AnyPacketChannel::is_read_open
125 [[nodiscard]] constexpr bool is_read_open() const;
126
127 // @copydoc AnyPacketChannel::is_write_open
128 [[nodiscard]] constexpr bool is_write_open() const;
129
130 // @copydoc AnyPacketChannel::is_read_or_write_open
131 [[nodiscard]] constexpr bool is_read_or_write_open() const {
132 return is_read_open() || is_write_open();
133 }
134
135 // Read API
136
137 // @copydoc AnyPacketChannel::PendRead
139
140 // Write API
141
142 // @copydoc AnyPacketChannel::PendReadyToWrite
144 size_t num = 1);
145 // @copydoc AnyPacketChannel::PendWrite
146 async2::Poll<> PendWrite(async2::Context& cx);
147
148 // @copydoc AnyPacketChannel::SetAvailableWrites
149 void SetAvailableWrites(uint16_t available_writes);
150 // @copydoc AnyPacketChannel::AcknowledgeWrites
151 void AcknowledgeWrites(uint16_t num_completed);
152
153 // @copydoc AnyPacketChannel::PendClose
155
156 // Conversions
157
159 template <typename Sibling,
160 typename = internal::EnableIfConvertible<PacketChannel, Sibling>>
161 constexpr operator Sibling&() {
162 return as<Sibling>();
163 }
164 template <typename Sibling,
165 typename = internal::EnableIfConvertible<PacketChannel, Sibling>>
166 constexpr operator const Sibling&() const {
167 return as<Sibling>();
168 }
169 constexpr operator AnyPacketChannel<Packet>&() {
170 return static_cast<AnyPacketChannel<Packet>&>(*this);
171 }
172 constexpr operator const AnyPacketChannel<Packet>&() const {
173 return static_cast<const AnyPacketChannel<Packet>&>(*this);
174 }
175
177 template <typename Sibling>
178 [[nodiscard]] Sibling& as() {
179 internal::CheckPacketChannelConversion<PacketChannel, Sibling>();
180 return static_cast<Sibling&>(static_cast<AnyPacketChannel<Packet>&>(*this));
181 }
182 template <typename Sibling>
183 [[nodiscard]] const Sibling& as() const {
184 internal::CheckPacketChannelConversion<PacketChannel, Sibling>();
185 return static_cast<const Sibling&>(
186 static_cast<const AnyPacketChannel<Packet>&>(*this));
187 }
188
191 template <Property... kOtherProperties>
192 [[nodiscard]] auto& as() {
193 return as<PacketChannel<Packet, kOtherProperties...>>();
194 }
195 template <Property... kOtherProperties>
196 [[nodiscard]] const auto& as() const {
197 return as<PacketChannel<Packet, kOtherProperties...>>();
198 }
199
200 protected:
201 // @copydoc AnyPacketChannel::GetAvailableWrites
202 uint16_t GetAvailableWrites() const;
203
204 private:
205 static_assert(internal::PacketChannelPropertiesAreValid<kProperties...>());
206
207 template <typename>
208 friend class AnyPacketChannel;
209
210 explicit constexpr PacketChannel() = default;
211};
212
219template <typename T>
220class AnyPacketChannel : private PacketChannel<T, kReadable>,
221 private PacketChannel<T, kWritable>,
222 private PacketChannel<T, kReadable, kWritable> {
223 public:
224 using Packet = T;
225
226 virtual ~AnyPacketChannel() = default;
227
229 [[nodiscard]] constexpr bool readable() const {
230 return (properties_ & kReadable) != 0u;
231 }
232
234 [[nodiscard]] constexpr bool writable() const {
235 return (properties_ & kWritable) != 0u;
236 }
237
240 [[nodiscard]] constexpr bool is_read_open() const {
241 return (read_write_open_ & kReadable) != 0u;
242 }
243
246 [[nodiscard]] constexpr bool is_write_open() const {
247 return (read_write_open_ & kWritable) != 0u;
248 }
249
251 [[nodiscard]] constexpr bool is_read_or_write_open() const {
252 return read_write_open_ != 0u;
253 }
254
274 // TODO: b/421962771 - if not readable, what to return (when called from
275 // Any*)? The is_read_open() prevents you from getting to the DoPendRead()
276 // that returns UNIMPLEMENTED.
277 if (!is_read_open()) {
278 return async2::Ready(Status::FailedPrecondition());
279 }
280 return DoPendRead(cx);
281 }
282
302 size_t num = 1);
303
319
327 void SetAvailableWrites(uint16_t available_writes);
328
334 void AcknowledgeWrites(uint16_t num_completed);
335
349
350 protected:
353 uint16_t GetAvailableWrites() const { return available_writes_; }
354
359 void set_read_closed() { read_write_open_ &= ~uint8_t{kReadable}; }
360
365 void set_write_closed() { read_write_open_ &= ~uint8_t{kWritable}; }
366
372 void set_read_write_closed() { read_write_open_ = 0; }
373
375 async2::Waker& write_waker() { return write_waker_; }
376
377 private:
378 template <typename, Property...>
379 friend class PacketChannel; // Allow static_casts to AnyPacketChannel
380
381 template <typename, Property...>
382 friend class internal::BasePacketChannelImpl; // Allow inheritance
383
385
386 explicit constexpr AnyPacketChannel(
387 uint8_t properties, uint16_t available_writes = kNoFlowControl)
388 : available_writes_(available_writes),
389 properties_(properties),
390 read_write_open_{uint8_t{kReadable} | uint8_t{kWritable}} {}
391
392 virtual async2::PollResult<Packet> DoPendRead(async2::Context& cx) = 0;
393
394 virtual async2::Poll<Status> DoPendReadyToWrite(async2::Context& cx,
395 size_t num) = 0;
396 virtual void DoStageWrite(Packet&& packet) = 0;
397 virtual async2::Poll<> DoPendWrite(async2::Context& cx) = 0;
398
399 virtual async2::Poll<Status> DoPendClose(async2::Context& cx) = 0;
400
401 async2::Waker write_waker_;
402 uint16_t available_writes_;
403 uint8_t properties_;
404 uint8_t read_write_open_;
405};
406
413template <typename Packet>
415
419template <typename Packet>
421
425template <typename Packet>
427
429
430// Extend this implement a `pw::channel::Packet`.
431template <typename ChannelType>
432class Implement;
433
434template <typename Packet, Property... kProperties>
435class Implement<PacketChannel<Packet, kProperties...>>
436 : public internal::PacketChannelImpl<Packet, kProperties...> {
437 protected:
438 explicit constexpr Implement() = default;
439};
440
441// Function implementations
442
443template <typename Packet>
446 PW_DASSERT(num > 0u);
447
448 if (!is_write_open()) {
449 return Status::FailedPrecondition();
450 }
451
452 if (GetAvailableWrites() < num) {
453 PW_ASYNC_STORE_WAKER(cx, write_waker_, "waiting for available writes");
454 return async2::Pending();
455 }
456 PW_TRY_READY_ASSIGN(Status ready, DoPendReadyToWrite(cx, num));
457 if (!ready.ok()) {
458 return ready;
459 }
460 return Result(PendingWrite<Packet>(*this, num));
461}
462
463template <typename Packet>
465 if (!is_write_open()) {
466 return async2::Ready();
467 }
468
469 if (GetAvailableWrites() == 0u) {
471 cx, write_waker_, "waiting for writes to be acknowledged");
472 return async2::Pending();
473 }
474
475 PW_TRY_READY(DoPendWrite(cx));
476 if (GetAvailableWrites() != kNoFlowControl) {
477 available_writes_ -= 1;
478 }
479
480 return async2::Ready();
481}
482
483template <typename Packet>
484void AnyPacketChannel<Packet>::SetAvailableWrites(uint16_t available_writes) {
485 if (available_writes > available_writes_) {
486 std::move(write_waker_).Wake();
487 }
488 available_writes_ = available_writes;
489}
490
491template <typename Packet>
493 PW_DASSERT(num_completed > 0u);
494 PW_DASSERT(GetAvailableWrites() != kNoFlowControl);
495
496 uint32_t new_available_writes = uint32_t{available_writes_} + num_completed;
497 PW_ASSERT(available_writes_ < kNoFlowControl);
498
499 available_writes_ = static_cast<uint16_t>(new_available_writes);
500
501 std::move(write_waker_).Wake();
502}
503
504template <typename Packet>
506 if (!is_read_or_write_open()) {
507 return OkStatus();
508 }
509 auto result = DoPendClose(cx);
510 if (result.IsReady()) {
511 set_read_write_closed();
512 }
513 std::move(write_waker_).Wake();
514 return result;
515}
516
517// pw::channel::PacketChannel<> function implementations. These down-cast to
518// AnyPacketChannel and call its functions, so must be defined after it.
519
520template <typename Packet, Property... kProperties>
522 return readable() &&
523 static_cast<const AnyPacketChannel<Packet>*>(this)->is_read_open();
524}
525
526template <typename Packet, Property... kProperties>
527constexpr bool PacketChannel<Packet, kProperties...>::is_write_open() const {
528 return writable() &&
529 static_cast<const AnyPacketChannel<Packet>*>(this)->is_write_open();
530}
531
532template <typename Packet, Property... kProperties>
533async2::PollResult<Packet> PacketChannel<Packet, kProperties...>::PendRead(
534 async2::Context& cx) {
535 static_assert(readable(), "PendRead may only be called on readable channels");
536 return static_cast<AnyPacketChannel<Packet>*>(this)->PendRead(cx);
537}
538
539template <typename Packet, Property... kProperties>
540async2::PollResult<PendingWrite<Packet>>
541PacketChannel<Packet, kProperties...>::PendReadyToWrite(async2::Context& cx,
542 size_t num) {
543 static_assert(writable(),
544 "PendReadyToWrite may only be called on writable channels");
545 return static_cast<AnyPacketChannel<Packet>*>(this)->PendReadyToWrite(cx,
546 num);
547}
548template <typename Packet, Property... kProperties>
549async2::Poll<> PacketChannel<Packet, kProperties...>::PendWrite(
550 async2::Context& cx) {
551 static_assert(writable(),
552 "PendWrite may only be called on writable channels");
553 return static_cast<AnyPacketChannel<Packet>*>(this)->PendWrite(cx);
554}
555template <typename Packet, Property... kProperties>
556uint16_t PacketChannel<Packet, kProperties...>::GetAvailableWrites() const {
557 static_assert(writable(),
558 "GetAvailableWrites may only be called on writable channels");
559 return static_cast<const AnyPacketChannel<Packet>&>(*this)
560 .GetAvailableWrites();
561}
562template <typename Packet, Property... kProperties>
563void PacketChannel<Packet, kProperties...>::SetAvailableWrites(
564 uint16_t available_writes) {
565 static_assert(writable(),
566 "SetAvailableWrites may only be called on writable channels");
567 return static_cast<AnyPacketChannel<Packet>*>(this)->SetAvailableWrites(
568 available_writes);
569}
570template <typename Packet, Property... kProperties>
571void PacketChannel<Packet, kProperties...>::AcknowledgeWrites(
572 uint16_t num_completed) {
573 static_assert(writable(),
574 "AcknowledgeWrites may only be called on writable channels");
575 return static_cast<AnyPacketChannel<Packet>*>(this)->AcknowledgeWrites(
576 num_completed);
577}
578
579template <typename Packet, Property... kProperties>
580async2::Poll<Status> PacketChannel<Packet, kProperties...>::PendClose(
581 async2::Context& cx) {
582 return static_cast<AnyPacketChannel<Packet>*>(this)->PendClose(cx);
583}
584
585namespace internal {
586
589template <typename Packet, Property... kProperties>
591 public:
592 using Channel = PacketChannel<Packet, kProperties...>;
593
594 Channel& channel() { return *this; }
595 const Channel& channel() const { return *this; }
596
598
599 // Use the base channel's version of the AnyPacketChannel API, so unsupported
600 // methods are disabled.
601 using Channel::PendRead;
602
603 using Channel::AcknowledgeWrites;
604 using Channel::GetAvailableWrites;
605 using Channel::PendReadyToWrite;
606 using Channel::PendWrite;
607 using Channel::SetAvailableWrites;
608
609 using Channel::PendClose;
610
611 private:
612 friend class PacketChannelImpl<Packet, kProperties...>;
613
614 constexpr BasePacketChannelImpl()
615 : AnyPacketChannel<Packet>((static_cast<uint8_t>(kProperties) | ...)) {}
616};
617
618template <typename Packet, Property... kProperties>
620 static_assert(PacketChannelPropertiesAreValid<kProperties...>());
621};
622
623// PacketChannelImpl specialization with no write support.
624template <typename Packet>
625class PacketChannelImpl<Packet, kReadable>
626 : public internal::BasePacketChannelImpl<Packet, kReadable> {
627 private:
628 async2::Poll<Status> DoPendReadyToWrite(async2::Context&, size_t) final {
629 return async2::Ready(Status::Unimplemented());
630 }
631 void DoStageWrite(Packet&&) final { PW_ASSERT(false); }
632 async2::Poll<> DoPendWrite(async2::Context&) final { return async2::Ready(); }
633};
634
635// PacketChannelImpl specialization with no read support.
636template <typename Packet>
637class PacketChannelImpl<Packet, kWritable>
638 : public internal::BasePacketChannelImpl<Packet, kWritable> {
639 private:
641 return async2::Ready(Status::Unimplemented());
642 }
643};
644
645// PacketChannelImpl specialization that supports reading and writing.
646template <typename Packet>
647class PacketChannelImpl<Packet, kReadable, kWritable>
648 : public internal::BasePacketChannelImpl<Packet, kReadable, kWritable> {};
649
650} // namespace internal
651} // namespace pw::channel
Definition: poll.h:25
Definition: status.h:86
constexpr bool ok() const
Definition: status.h:158
Definition: context.h:55
Definition: poll.h:60
Definition: waker.h:160
Definition: packet_channel.h:222
Definition: channel.h:583
Definition: packet_channel.h:111
auto & as()
Definition: packet_channel.h:192
Definition: packet_channel.h:48
Definition: packet_channel.h:590
Definition: packet_channel.h:619
#define PW_TRY_READY_ASSIGN(lhs, expression)
Definition: try.h:28
#define PW_TRY_READY(expr)
Returns Poll::Pending() if expr is Poll::Pending().
Definition: try.h:19
constexpr PendingType Pending()
Returns a value indicating that an operation was not yet able to complete.
Definition: poll.h:271
#define PW_ASYNC_STORE_WAKER(context, waker_or_queue_out, wait_reason_string)
Definition: waker.h:60
constexpr Poll Ready()
Returns a value indicating completion.
Definition: poll.h:255
constexpr uint16_t kNoFlowControl
Definition: packet_channel.h:96
void Stage(Packet &&packet)
Definition: packet_channel.h:73
void set_write_closed()
Definition: packet_channel.h:365
async2::Poll PendWrite(async2::Context &cx)
Definition: packet_channel.h:464
void SetAvailableWrites(uint16_t available_writes)
Definition: packet_channel.h:484
uint16_t GetAvailableWrites() const
Definition: packet_channel.h:353
Sibling & as()
Returns a reference to this as another compatible packet channel type.
Definition: packet_channel.h:178
constexpr bool is_read_open() const
Definition: packet_channel.h:240
async2::PollResult< Packet > PendRead(async2::Context &cx)
Definition: packet_channel.h:273
async2::Poll< Status > PendClose(async2::Context &cx)
Definition: packet_channel.h:505
constexpr bool is_write_open() const
Definition: packet_channel.h:246
void set_read_closed()
Definition: packet_channel.h:359
constexpr bool is_read_or_write_open() const
True if the channel is open for either reading or writing.
Definition: packet_channel.h:251
constexpr bool readable() const
Returns whether the channel implementation is readable.
Definition: packet_channel.h:229
void AcknowledgeWrites(uint16_t num_completed)
Definition: packet_channel.h:492
void set_read_write_closed()
Definition: packet_channel.h:372
async2::PollResult< PendingWrite< Packet > > PendReadyToWrite(async2::Context &cx, size_t num=1)
Definition: packet_channel.h:445
constexpr bool writable() const
Returns whether the channel implementation is writable.
Definition: packet_channel.h:234
async2::Waker & write_waker()
Allows implementations to access the write waker.
Definition: packet_channel.h:375
constexpr Status OkStatus()
Definition: status.h:235