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