Pigweed
C/C++ API Reference
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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
45template <typename Packet>
47 public:
48 PendingWrite(const PendingWrite&) = delete;
49 PendingWrite& operator=(const PendingWrite&) = delete;
50
51 constexpr PendingWrite(PendingWrite&& other)
52 : channel_(other.channel_),
53 num_packets_(cpp20::exchange(other.num_packets_, 0u)) {}
54 constexpr PendingWrite& operator=(PendingWrite&& other) {
55 channel_ = other.channel_;
56 num_packets_ = cpp20::exchange(other.num_packets_, 0u);
57 }
58
60 // TODO: b/421961717 - Consider allowing staged writes to be discarded
61 PW_ASSERT(num_packets_ == 0u);
62 }
63
68 void Stage(Packet&& packet) {
69 PW_ASSERT(num_packets_ > 0u);
70 channel_->DoStageWrite(std::move(packet));
71 num_packets_ -= 1;
72 }
73
74 size_t num_packets() const { return num_packets_; }
75
76 private:
77 friend class AnyPacketChannel<Packet>;
78
79 constexpr PendingWrite(AnyPacketChannel<Packet>& channel, size_t num_packets)
80 : channel_(&channel), num_packets_(num_packets) {}
81
82 AnyPacketChannel<Packet>* channel_;
83 size_t num_packets_;
84};
85
88
91inline constexpr uint16_t kNoFlowControl = std::numeric_limits<uint16_t>::max();
92
105template <typename T, Property... kProperties>
107 public:
108 using Packet = T;
109
110 // @copydoc AnyPacketChannel::readable
111 [[nodiscard]] static constexpr bool readable() {
112 return ((kProperties == Property::kReadable) || ...);
113 }
114 // @copydoc AnyPacketChannel::writable
115 [[nodiscard]] static constexpr bool writable() {
116 return ((kProperties == Property::kWritable) || ...);
117 }
118
119 // @copydoc AnyPacketChannel::is_read_open
120 [[nodiscard]] constexpr bool is_read_open() const;
121
122 // @copydoc AnyPacketChannel::is_write_open
123 [[nodiscard]] constexpr bool is_write_open() const;
124
125 // @copydoc AnyPacketChannel::is_read_or_write_open
126 [[nodiscard]] constexpr bool is_read_or_write_open() const {
127 return is_read_open() || is_write_open();
128 }
129
130 // Read API
131
132 // @copydoc AnyPacketChannel::PendRead
134
135 // Write API
136
137 // @copydoc AnyPacketChannel::PendReadyToWrite
139 async2::Context& cx, size_t num = 1);
140 // @copydoc AnyPacketChannel::PendWrite
141 async2::Poll<> PendWrite(async2::Context& cx);
142
143 // @copydoc AnyPacketChannel::SetAvailableWrites
144 void SetAvailableWrites(uint16_t available_writes);
145 // @copydoc AnyPacketChannel::AcknowledgeWrites
146 void AcknowledgeWrites(uint16_t num_completed);
147
148 // @copydoc AnyPacketChannel::PendClose
150
151 // Conversions
152
154 template <typename Sibling,
155 typename = internal::EnableIfConvertible<PacketChannel, Sibling>>
156 constexpr operator Sibling&() {
157 return as<Sibling>();
158 }
159 template <typename Sibling,
160 typename = internal::EnableIfConvertible<PacketChannel, Sibling>>
161 constexpr operator const Sibling&() const {
162 return as<Sibling>();
163 }
164 constexpr operator AnyPacketChannel<Packet>&() {
165 return static_cast<AnyPacketChannel<Packet>&>(*this);
166 }
167 constexpr operator const AnyPacketChannel<Packet>&() const {
168 return static_cast<const AnyPacketChannel<Packet>&>(*this);
169 }
170
172 template <typename Sibling>
173 [[nodiscard]] Sibling& as() {
174 internal::CheckPacketChannelConversion<PacketChannel, Sibling>();
175 return static_cast<Sibling&>(static_cast<AnyPacketChannel<Packet>&>(*this));
176 }
177 template <typename Sibling>
178 [[nodiscard]] const Sibling& as() const {
179 internal::CheckPacketChannelConversion<PacketChannel, Sibling>();
180 return static_cast<const Sibling&>(
181 static_cast<const AnyPacketChannel<Packet>&>(*this));
182 }
183
186 template <Property... kOtherProperties>
187 [[nodiscard]] auto& as() {
188 return as<PacketChannel<Packet, kOtherProperties...>>();
189 }
190 template <Property... kOtherProperties>
191 [[nodiscard]] const auto& as() const {
192 return as<PacketChannel<Packet, kOtherProperties...>>();
193 }
194
195 protected:
196 // @copydoc AnyPacketChannel::GetAvailableWrites
197 uint16_t GetAvailableWrites() const;
198
199 private:
200 static_assert(internal::PacketChannelPropertiesAreValid<kProperties...>());
201
202 template <typename>
203 friend class AnyPacketChannel;
204
205 explicit constexpr PacketChannel() = default;
206};
207
214template <typename T>
215class AnyPacketChannel : private PacketChannel<T, kReadable>,
216 private PacketChannel<T, kWritable>,
217 private PacketChannel<T, kReadable, kWritable> {
218 public:
219 using Packet = T;
220
221 virtual ~AnyPacketChannel() = default;
222
224 [[nodiscard]] constexpr bool readable() const {
225 return (properties_ & kReadable) != 0u;
226 }
227
229 [[nodiscard]] constexpr bool writable() const {
230 return (properties_ & kWritable) != 0u;
231 }
232
235 [[nodiscard]] constexpr bool is_read_open() const {
236 return (read_write_open_ & kReadable) != 0u;
237 }
238
241 [[nodiscard]] constexpr bool is_write_open() const {
242 return (read_write_open_ & kWritable) != 0u;
243 }
244
246 [[nodiscard]] constexpr bool is_read_or_write_open() const {
247 return read_write_open_ != 0u;
248 }
249
269 // TODO: b/421962771 - if not readable, what to return (when called from
270 // Any*)? The is_read_open() prevents you from getting to the DoPendRead()
271 // that returns UNIMPLEMENTED.
272 if (!is_read_open()) {
273 return async2::Ready(Status::FailedPrecondition());
274 }
275 return DoPendRead(cx);
276 }
277
297 async2::Context& cx, size_t num = 1);
298
314
322 void SetAvailableWrites(uint16_t available_writes);
323
329 void AcknowledgeWrites(uint16_t num_completed);
330
344
345 protected:
348 uint16_t GetAvailableWrites() const { return available_writes_; }
349
354 void set_read_closed() { read_write_open_ &= ~uint8_t{kReadable}; }
355
360 void set_write_closed() { read_write_open_ &= ~uint8_t{kWritable}; }
361
367 void set_read_write_closed() { read_write_open_ = 0; }
368
370 async2::Waker& write_waker() { return write_waker_; }
371
372 private:
373 template <typename, Property...>
374 friend class PacketChannel; // Allow static_casts to AnyPacketChannel
375
376 template <typename, Property...>
377 friend class internal::BasePacketChannelImpl; // Allow inheritance
378
380
381 explicit constexpr AnyPacketChannel(
382 uint8_t properties, uint16_t available_writes = kNoFlowControl)
383 : available_writes_(available_writes),
384 properties_(properties),
385 read_write_open_{uint8_t{kReadable} | uint8_t{kWritable}} {}
386
387 virtual async2::Poll<Result<Packet>> DoPendRead(async2::Context& cx) = 0;
388
389 virtual async2::Poll<Status> DoPendReadyToWrite(async2::Context& cx,
390 size_t num) = 0;
391 virtual void DoStageWrite(Packet&& packet) = 0;
392 virtual async2::Poll<> DoPendWrite(async2::Context& cx) = 0;
393
394 virtual async2::Poll<Status> DoPendClose(async2::Context& cx) = 0;
395
396 async2::Waker write_waker_;
397 uint16_t available_writes_;
398 uint8_t properties_;
399 uint8_t read_write_open_;
400};
401
408template <typename Packet>
410
414template <typename Packet>
416
420template <typename Packet>
422
424
425// Extend this implement a `pw::channel::Packet`.
426template <typename ChannelType>
427class Implement;
428
429template <typename Packet, Property... kProperties>
430class Implement<PacketChannel<Packet, kProperties...>>
431 : public internal::PacketChannelImpl<Packet, kProperties...> {
432 protected:
433 explicit constexpr Implement() = default;
434};
435
436// Function implementations
437
438template <typename Packet>
441 PW_DASSERT(num > 0u);
442
443 if (!is_write_open()) {
444 return Status::FailedPrecondition();
445 }
446
447 if (GetAvailableWrites() < num) {
448 PW_ASYNC_STORE_WAKER(cx, write_waker_, "waiting for available writes");
449 return async2::Pending();
450 }
451 PW_TRY_READY_ASSIGN(Status ready, DoPendReadyToWrite(cx, num));
452 if (!ready.ok()) {
453 return ready;
454 }
455 return Result(PendingWrite<Packet>(*this, num));
456}
457
458template <typename Packet>
460 if (!is_write_open()) {
461 return async2::Ready();
462 }
463
464 if (GetAvailableWrites() == 0u) {
465 PW_ASYNC_STORE_WAKER(
466 cx, write_waker_, "waiting for writes to be acknowledged");
467 return async2::Pending();
468 }
469
470 PW_TRY_READY(DoPendWrite(cx));
471 if (GetAvailableWrites() != kNoFlowControl) {
472 available_writes_ -= 1;
473 }
474
475 return async2::Ready();
476}
477
478template <typename Packet>
479void AnyPacketChannel<Packet>::SetAvailableWrites(uint16_t available_writes) {
480 if (available_writes > available_writes_) {
481 std::move(write_waker_).Wake();
482 }
483 available_writes_ = available_writes;
484}
485
486template <typename Packet>
488 PW_DASSERT(num_completed > 0u);
489 PW_DASSERT(GetAvailableWrites() != kNoFlowControl);
490
491 uint32_t new_available_writes = uint32_t{available_writes_} + num_completed;
492 PW_ASSERT(available_writes_ < kNoFlowControl);
493
494 available_writes_ = static_cast<uint16_t>(new_available_writes);
495
496 std::move(write_waker_).Wake();
497}
498
499template <typename Packet>
501 if (!is_read_or_write_open()) {
502 return OkStatus();
503 }
504 auto result = DoPendClose(cx);
505 if (result.IsReady()) {
506 set_read_write_closed();
507 }
508 std::move(write_waker_).Wake();
509 return result;
510}
511
512// pw::channel::PacketChannel<> function implementations. These down-cast to
513// AnyPacketChannel and call its functions, so must be defined after it.
514
515template <typename Packet, Property... kProperties>
517 return readable() &&
518 static_cast<const AnyPacketChannel<Packet>*>(this)->is_read_open();
519}
520
521template <typename Packet, Property... kProperties>
522constexpr bool PacketChannel<Packet, kProperties...>::is_write_open() const {
523 return writable() &&
524 static_cast<const AnyPacketChannel<Packet>*>(this)->is_write_open();
525}
526
527template <typename Packet, Property... kProperties>
528async2::Poll<Result<Packet>> PacketChannel<Packet, kProperties...>::PendRead(
529 async2::Context& cx) {
530 static_assert(readable(), "PendRead may only be called on readable channels");
531 return static_cast<AnyPacketChannel<Packet>*>(this)->PendRead(cx);
532}
533
534template <typename Packet, Property... kProperties>
535async2::Poll<Result<PendingWrite<Packet>>>
536PacketChannel<Packet, kProperties...>::PendReadyToWrite(async2::Context& cx,
537 size_t num) {
538 static_assert(writable(),
539 "PendReadyToWrite may only be called on writable channels");
540 return static_cast<AnyPacketChannel<Packet>*>(this)->PendReadyToWrite(cx,
541 num);
542}
543template <typename Packet, Property... kProperties>
544async2::Poll<> PacketChannel<Packet, kProperties...>::PendWrite(
545 async2::Context& cx) {
546 static_assert(writable(),
547 "PendWrite may only be called on writable channels");
548 return static_cast<AnyPacketChannel<Packet>*>(this)->PendWrite(cx);
549}
550template <typename Packet, Property... kProperties>
551uint16_t PacketChannel<Packet, kProperties...>::GetAvailableWrites() const {
552 static_assert(writable(),
553 "GetAvailableWrites may only be called on writable channels");
554 return static_cast<const AnyPacketChannel<Packet>&>(*this)
555 .GetAvailableWrites();
556}
557template <typename Packet, Property... kProperties>
558void PacketChannel<Packet, kProperties...>::SetAvailableWrites(
559 uint16_t available_writes) {
560 static_assert(writable(),
561 "SetAvailableWrites may only be called on writable channels");
562 return static_cast<AnyPacketChannel<Packet>*>(this)->SetAvailableWrites(
563 available_writes);
564}
565template <typename Packet, Property... kProperties>
566void PacketChannel<Packet, kProperties...>::AcknowledgeWrites(
567 uint16_t num_completed) {
568 static_assert(writable(),
569 "AcknowledgeWrites may only be called on writable channels");
570 return static_cast<AnyPacketChannel<Packet>*>(this)->AcknowledgeWrites(
571 num_completed);
572}
573
574template <typename Packet, Property... kProperties>
575async2::Poll<Status> PacketChannel<Packet, kProperties...>::PendClose(
576 async2::Context& cx) {
577 return static_cast<AnyPacketChannel<Packet>*>(this)->PendClose(cx);
578}
579
580namespace internal {
581
584template <typename Packet, Property... kProperties>
586 public:
587 using Channel = PacketChannel<Packet, kProperties...>;
588
589 Channel& channel() { return *this; }
590 const Channel& channel() const { return *this; }
591
593
594 // Use the base channel's version of the AnyPacketChannel API, so unsupported
595 // methods are disabled.
596 using Channel::PendRead;
597
598 using Channel::AcknowledgeWrites;
599 using Channel::GetAvailableWrites;
600 using Channel::PendReadyToWrite;
601 using Channel::PendWrite;
602 using Channel::SetAvailableWrites;
603
604 using Channel::PendClose;
605
606 private:
607 friend class PacketChannelImpl<Packet, kProperties...>;
608
609 constexpr BasePacketChannelImpl()
610 : AnyPacketChannel<Packet>((static_cast<uint8_t>(kProperties) | ...)) {}
611};
612
613template <typename Packet, Property... kProperties>
615 static_assert(PacketChannelPropertiesAreValid<kProperties...>());
616};
617
618// PacketChannelImpl specialization with no write support.
619template <typename Packet>
620class PacketChannelImpl<Packet, kReadable>
621 : public internal::BasePacketChannelImpl<Packet, kReadable> {
622 private:
623 async2::Poll<Status> DoPendReadyToWrite(async2::Context&, size_t) final {
624 return async2::Ready(Status::Unimplemented());
625 }
626 void DoStageWrite(Packet&&) final { PW_ASSERT(false); }
627 async2::Poll<> DoPendWrite(async2::Context&) final { return async2::Ready(); }
628};
629
630// PacketChannelImpl specialization with no read support.
631template <typename Packet>
632class PacketChannelImpl<Packet, kWritable>
633 : public internal::BasePacketChannelImpl<Packet, kWritable> {
634 private:
635 async2::Poll<Result<Packet>> DoPendRead(async2::Context&) final {
636 return async2::Ready(Status::Unimplemented());
637 }
638};
639
640// PacketChannelImpl specialization that supports reading and writing.
641template <typename Packet>
642class PacketChannelImpl<Packet, kReadable, kWritable>
643 : public internal::BasePacketChannelImpl<Packet, kReadable, kWritable> {};
644
645} // namespace internal
646} // namespace pw::channel
Definition: status.h:85
constexpr bool ok() const
Definition: status.h:157
Definition: context.h:53
Definition: poll.h:54
Definition: waker.h:154
Definition: packet_channel.h:217
async2::Poll< Result< PendingWrite< Packet > > > PendReadyToWrite(async2::Context &cx, size_t num=1)
Definition: packet_channel.h:440
void set_write_closed()
Definition: packet_channel.h:360
async2::Poll PendWrite(async2::Context &cx)
Definition: packet_channel.h:459
async2::Poll< Result< Packet > > PendRead(async2::Context &cx)
Definition: packet_channel.h:268
void SetAvailableWrites(uint16_t available_writes)
Definition: packet_channel.h:479
uint16_t GetAvailableWrites() const
Definition: packet_channel.h:348
constexpr bool is_read_open() const
Definition: packet_channel.h:235
async2::Poll< Status > PendClose(async2::Context &cx)
Definition: packet_channel.h:500
constexpr bool is_write_open() const
Definition: packet_channel.h:241
void set_read_closed()
Definition: packet_channel.h:354
constexpr bool is_read_or_write_open() const
True if the channel is open for either reading or writing.
Definition: packet_channel.h:246
constexpr bool readable() const
Returns whether the channel implementation is readable.
Definition: packet_channel.h:224
void AcknowledgeWrites(uint16_t num_completed)
Definition: packet_channel.h:487
void set_read_write_closed()
Definition: packet_channel.h:367
constexpr bool writable() const
Returns whether the channel implementation is writable.
Definition: packet_channel.h:229
async2::Waker & write_waker()
Allows implementations to access the write waker.
Definition: packet_channel.h:370
Definition: channel.h:583
Definition: packet_channel.h:106
Sibling & as()
Returns a reference to this as another compatible packet channel type.
Definition: packet_channel.h:173
auto & as()
Definition: packet_channel.h:187
Definition: packet_channel.h:46
void Stage(Packet &&packet)
Definition: packet_channel.h:68
Definition: packet_channel.h:585
Definition: packet_channel.h:614
constexpr uint16_t kNoFlowControl
Definition: packet_channel.h:91
constexpr Status OkStatus()
Definition: status.h:234