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