xref: /aosp_15_r20/external/pigweed/pw_channel/public/pw_channel/forwarding_channel.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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 <mutex>
18 #include <optional>
19 
20 #include "pw_async2/dispatcher.h"
21 #include "pw_async2/poll.h"
22 #include "pw_channel/channel.h"
23 #include "pw_multibuf/allocator.h"
24 #include "pw_sync/lock_annotations.h"
25 #include "pw_sync/mutex.h"
26 
27 namespace pw::channel {
28 namespace internal {
29 
30 // Internal Channel implementation for use with ForwardingChannelPair. It is
31 // specialized for kDatagram and kByte.
32 template <DataType kType>
33 class ForwardingChannel;
34 
35 }  // namespace internal
36 
37 /// @defgroup pw_channel_forwarding
38 /// @{
39 
40 /// Forwards either datagrams or bytes between two channels. Writes to the first
41 /// channel appear as reads on the second, and vice versa.
42 ///
43 /// `ForwardingChannelPair` enables connecting two subsystems that communicate
44 /// with channels without implementing a custom channel.
45 template <DataType kType>
46 class ForwardingChannelPair {
47  public:
48   explicit constexpr ForwardingChannelPair(
49       multibuf::MultiBufAllocator& first_write_alloc,
50       multibuf::MultiBufAllocator& second_write_alloc);
51 
52   ForwardingChannelPair(const ForwardingChannelPair&) = delete;
53   ForwardingChannelPair& operator=(const ForwardingChannelPair&) = delete;
54 
55   ForwardingChannelPair(ForwardingChannelPair&&) = delete;
56   ForwardingChannelPair& operator=(ForwardingChannelPair&&) = delete;
57 
58   /// Returns the first channel in the pair.
first()59   Channel<kType, kReliable, kReadable, kWritable>& first() {
60     return first_.channel();
61   }
62 
63   /// Returns a const reference to the first channel in the pair.
first()64   const Channel<kType, kReliable, kReadable, kWritable>& first() const {
65     return first_;
66   }
67 
68   /// Returns the second channel in the pair.
second()69   Channel<kType, kReliable, kReadable, kWritable>& second() {
70     return second_.channel();
71   }
72 
73   /// Returns a const reference to the second channel in the pair.
second()74   const Channel<kType, kReliable, kReadable, kWritable>& second() const {
75     return second_;
76   }
77 
78  private:
79   template <DataType>
80   friend class internal::ForwardingChannel;
81 
82   sync::Mutex mutex_;
83   // These channels refer to each other, so their lifetimes must match.
84   internal::ForwardingChannel<kType> first_;
85   internal::ForwardingChannel<kType> second_;
86 };
87 
88 /// Alias for a pair of forwarding datagram channels.
89 using ForwardingDatagramChannelPair =
90     ForwardingChannelPair<DataType::kDatagram>;
91 
92 /// Alias for a pair of forwarding byte channels.
93 using ForwardingByteChannelPair = ForwardingChannelPair<DataType::kByte>;
94 
95 /// @}
96 
97 namespace internal {
98 
99 template <>
100 class ForwardingChannel<DataType::kDatagram>
101     : public Implement<ReliableDatagramReaderWriter> {
102  public:
103   ForwardingChannel(const ForwardingChannel&) = delete;
104   ForwardingChannel& operator=(const ForwardingChannel&) = delete;
105 
106   ForwardingChannel(ForwardingChannel&&) = delete;
107   ForwardingChannel& operator=(ForwardingChannel&&) = delete;
108 
109  private:
110   friend class ForwardingChannelPair<DataType::kDatagram>;
111 
ForwardingChannel(ForwardingChannelPair<DataType::kDatagram> & pair,ForwardingChannel * sibling,multibuf::MultiBufAllocator & write_alloc)112   constexpr ForwardingChannel(ForwardingChannelPair<DataType::kDatagram>& pair,
113                               ForwardingChannel* sibling,
114                               multibuf::MultiBufAllocator& write_alloc)
115       : pair_(pair), sibling_(*sibling), write_alloc_future_(write_alloc) {}
116 
117   async2::Poll<Result<multibuf::MultiBuf>> DoPendRead(
118       async2::Context& cx) override;
119 
120   async2::Poll<Status> DoPendReadyToWrite(async2::Context& cx) override;
121 
DoPendAllocateWriteBuffer(async2::Context & cx,size_t min_bytes)122   async2::Poll<std::optional<multibuf::MultiBuf>> DoPendAllocateWriteBuffer(
123       async2::Context& cx, size_t min_bytes) override {
124     write_alloc_future_.SetDesiredSize(min_bytes);
125     return write_alloc_future_.Pend(cx);
126   }
127 
128   Status DoStageWrite(multibuf::MultiBuf&& data) override;
129 
DoPendWrite(async2::Context &)130   async2::Poll<Status> DoPendWrite(async2::Context&) override {
131     return OkStatus();
132   }
133 
134   async2::Poll<Status> DoPendClose(async2::Context&) override;
135 
136   // The two channels share one mutex. Lock safty analysis doesn't understand
137   // that, so has to be disabled for some functions.
138   ForwardingChannelPair<DataType::kDatagram>& pair_;
139   ForwardingChannel& sibling_;
140 
141   // Could use a queue here.
142   std::optional<multibuf::MultiBuf> read_queue_ PW_GUARDED_BY(pair_.mutex_);
143   async2::Waker waker_ PW_GUARDED_BY(pair_.mutex_);
144   multibuf::MultiBufAllocationFuture write_alloc_future_;
145 };
146 
147 template <>
148 class ForwardingChannel<DataType::kByte>
149     : public Implement<ReliableByteReaderWriter> {
150  public:
151   ForwardingChannel(const ForwardingChannel&) = delete;
152   ForwardingChannel& operator=(const ForwardingChannel&) = delete;
153 
154   ForwardingChannel(ForwardingChannel&&) = delete;
155   ForwardingChannel& operator=(ForwardingChannel&&) = delete;
156 
157  private:
158   friend class ForwardingChannelPair<DataType::kByte>;
159 
ForwardingChannel(ForwardingChannelPair<DataType::kByte> & pair,ForwardingChannel * sibling,multibuf::MultiBufAllocator & write_alloc)160   constexpr ForwardingChannel(ForwardingChannelPair<DataType::kByte>& pair,
161                               ForwardingChannel* sibling,
162                               multibuf::MultiBufAllocator& write_alloc)
163       : pair_(pair), sibling_(*sibling), write_alloc_future_(write_alloc) {}
164 
165   async2::Poll<Result<multibuf::MultiBuf>> DoPendRead(
166       async2::Context& cx) override;
167 
DoPendReadyToWrite(async2::Context &)168   async2::Poll<Status> DoPendReadyToWrite(async2::Context&) override {
169     return async2::Ready(OkStatus());
170   }
171 
DoPendAllocateWriteBuffer(async2::Context & cx,size_t min_bytes)172   async2::Poll<std::optional<multibuf::MultiBuf>> DoPendAllocateWriteBuffer(
173       async2::Context& cx, size_t min_bytes) override {
174     write_alloc_future_.SetDesiredSize(min_bytes);
175     return write_alloc_future_.Pend(cx);
176   }
177 
178   Status DoStageWrite(multibuf::MultiBuf&& data) override;
179 
DoPendWrite(async2::Context &)180   async2::Poll<Status> DoPendWrite(async2::Context&) override {
181     return OkStatus();
182   }
183 
184   async2::Poll<Status> DoPendClose(async2::Context&) override;
185 
186   ForwardingChannelPair<DataType::kByte>& pair_;
187   ForwardingChannel& sibling_;
188 
189   multibuf::MultiBuf read_queue_ PW_GUARDED_BY(pair_.mutex_);
190   async2::Waker read_waker_ PW_GUARDED_BY(pair_.mutex_);
191   multibuf::MultiBufAllocationFuture write_alloc_future_;
192 };
193 
194 }  // namespace internal
195 
196 // Define the constructor out-of-line, after ForwardingChannel is defined.
197 template <DataType kType>
ForwardingChannelPair(multibuf::MultiBufAllocator & first_write_allocator,multibuf::MultiBufAllocator & second_write_allocator)198 constexpr ForwardingChannelPair<kType>::ForwardingChannelPair(
199     multibuf::MultiBufAllocator& first_write_allocator,
200     multibuf::MultiBufAllocator& second_write_allocator)
201     : first_(*this, &second_, first_write_allocator),
202       second_(*this, &first_, second_write_allocator) {}
203 
204 }  // namespace pw::channel
205