• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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