• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_
18 #define INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_
19 
20 #include <optional>
21 
22 #include <variant>
23 #include "perfetto/base/flat_set.h"
24 #include "perfetto/base/platform_handle.h"
25 
26 namespace perfetto {
27 namespace base {
28 
29 // Forward declarations.
30 class PollContext;
31 
32 // "Void" type for futures: this type can be used when a Future/Stream wants
33 // to return no value. We cannot use void directly because it causes all sorts
34 // of subtle issues with templates.
35 struct FVoid {};
36 
37 // Indicates that the Future is not ready to produce data at the moment but
38 // will do so at a later date.
39 struct PendingPollResult {};
40 
41 // Return value of Future<T>::Poll.
42 //
43 // Essentially a wrapper around std::variant<T, PendingPollResult> but with
44 // higher level API.
45 template <typename T>
46 class FuturePollResult {
47  public:
48   using PollT = T;
49 
50   // Intentionally implicit to allow idiomatic returns.
FuturePollResult(const PendingPollResult &)51   FuturePollResult(const PendingPollResult&) : inner_(PendingPollResult()) {}
FuturePollResult(T item)52   FuturePollResult(T item) noexcept : inner_(std::move(item)) {}
53 
54   // Returns whether the Future is still pending.
IsPending()55   bool IsPending() const {
56     return std::holds_alternative<PendingPollResult>(inner_);
57   }
58 
59   // The real value inside this result: requires !IsPending().
item()60   T& item() {
61     PERFETTO_DCHECK(!IsPending());
62     return std::get<T>(inner_);
63   }
item()64   const T& item() const {
65     PERFETTO_DCHECK(!IsPending());
66     return std::get<T>(inner_);
67   }
68 
69   // The real value inside this result: requires !IsPending().
70   T* operator->() { return &item(); }
71   const T* operator->() const { return &item(); }
72 
73  private:
74   std::variant<PendingPollResult, T> inner_;
75 };
76 
77 // Interface for implementing the Future<T>::Poll function.
78 //
79 // This is essentially a variant of the common PIMPL (pointer to impl) pattern
80 // used in C++ to allow having different implementations for Future<T>::Poll.
81 //
82 // We are using this instead of having an abstract function in Future to avoid
83 // having to wrap Future in unique_ptr everywhere it's used.
84 //
85 // We could have used std::function<Result(PollContext*)> but not all
86 // implementations of FuturePollable are copyable. If we had C++23, we could use
87 // std::move_only_function but we are some years from being able to do that.
88 template <typename T>
89 class FuturePollable {
90  public:
91   using PollT = T;
92 
93   virtual ~FuturePollable() = default;
94 
95   // Implementation of the Poll function of a Future: see Future documentation
96   // for how this should be implemented.
97   virtual FuturePollResult<T> Poll(PollContext*) = 0;
98 };
99 
100 // Indicates that the Stream has been exhausted and no more values will be
101 // returned.
102 struct DonePollResult {};
103 
104 // Return value of Stream<T>::Poll.
105 //
106 // Essentially a wrapper around std::variant<T, PendingPollResult,
107 // DonePollResult> but with higher level API.
108 template <typename T>
109 class StreamPollResult {
110  public:
111   using PollT = T;
112 
113   // Intentionally implicit to allow idiomatic returns.
StreamPollResult(const PendingPollResult &)114   StreamPollResult(const PendingPollResult&) : inner_(PendingPollResult()) {}
StreamPollResult(const DonePollResult &)115   StreamPollResult(const DonePollResult&) : inner_(DonePollResult()) {}
StreamPollResult(T item)116   StreamPollResult(T item) : inner_(std::move(item)) {}
117 
118   // Returns whether the Stream is still pending.
IsPending()119   bool IsPending() const {
120     return std::holds_alternative<PendingPollResult>(inner_);
121   }
122 
123   // Returns whether the Stream is done.
IsDone()124   bool IsDone() const { return std::holds_alternative<DonePollResult>(inner_); }
125 
126   // The real value inside this result: requires !IsPending() and !IsDone().
item()127   T& item() {
128     PERFETTO_DCHECK(!IsPending());
129     PERFETTO_DCHECK(!IsDone());
130     return std::get<T>(inner_);
131   }
item()132   const T& item() const {
133     PERFETTO_DCHECK(!IsPending());
134     PERFETTO_DCHECK(!IsDone());
135     return std::get<T>(inner_);
136   }
137 
138   // The real value inside this result: requires !IsPending() and !IsDone().
139   T* operator->() { return &item(); }
140   const T* operator->() const { return &item(); }
141 
142  private:
143   std::variant<PendingPollResult, DonePollResult, T> inner_;
144 };
145 
146 // Interface for implementing the Stream<T>::Poll function.
147 //
148 // This is essentially analagous to FuturePollable<T> for streams: check the
149 // documentation of that class for why this exists.
150 template <typename T>
151 class StreamPollable {
152  public:
153   using PollT = T;
154 
155   virtual ~StreamPollable() = default;
156 
157   // Implementation of the Poll function of a Stream: see Stream documentation
158   // for how this should be implemented.
159   virtual StreamPollResult<T> PollNext(PollContext*) = 0;
160 };
161 
162 // Context class passed to Pollable classes.
163 //
164 // Implementations of Pollable which simply wrap another Pollable will use
165 // this as an opaque parameter to pass on.
166 //
167 // "Source" pollables (i.e. Pollables dealing directly with FDs) should call
168 // |RegisterInterested| when the FD returns EAGAIN/EWOULDBLOCK with the
169 // PollContext passed in.
170 class PollContext {
171  public:
PollContext(FlatSet<PlatformHandle> * interested_handles,const FlatSet<PlatformHandle> * ready_handles)172   explicit PollContext(FlatSet<PlatformHandle>* interested_handles,
173                        const FlatSet<PlatformHandle>* ready_handles)
174       : interested_handles_(interested_handles),
175         ready_handles_(ready_handles) {}
176 
177   PollContext(PollContext&&) = default;
178   PollContext& operator=(PollContext&&) = default;
179 
180   // Called by implementations of Future<T> to indicate that Poll should be
181   // called again when |handle(s)| are ready for reading (or have been closed).
RegisterInterested(PlatformHandle handle)182   void RegisterInterested(PlatformHandle handle) {
183     interested_handles_->insert(handle);
184   }
RegisterAllInterested(const FlatSet<PlatformHandle> & handles)185   void RegisterAllInterested(const FlatSet<PlatformHandle>& handles) {
186     for (PlatformHandle handle : handles) {
187       RegisterInterested(handle);
188     }
189   }
190 
191   // Returns a set of all the fds which were marked as "ready" by the operating
192   // system (i.e. POLLIN/POLLHUP on Linux).
ready_handles()193   const FlatSet<PlatformHandle>& ready_handles() const {
194     return *ready_handles_;
195   }
196 
197  private:
198   PollContext(const PollContext&) = delete;
199   PollContext& operator=(const PollContext&) = delete;
200 
201   FlatSet<PlatformHandle>* interested_handles_ = nullptr;
202   const FlatSet<PlatformHandle>* ready_handles_ = nullptr;
203 };
204 
205 // Evaluates |expr|, which should return a FuturePollResult. If IsPending is
206 // true, returns base::PendingPollResult().
207 //
208 // Example usage:
209 //
210 // Future<int> MyIntReturningFutureFn();
211 //
212 // FuturePollResult<std::string> Poll(PollContext* ctx) {
213 //   // res will be of type "int"
214 //   ASSIGN_OR_RETURN_IF_PENDING_FUTURE(res, MyIntReturningFutureFn());
215 //   return std::to_string(*foo);
216 // }
217 #define ASSIGN_OR_RETURN_IF_PENDING_FUTURE(var, expr) \
218   auto assign_and_return_if_poll_##var = (expr);      \
219   if (assign_and_return_if_poll_##var.IsPending())    \
220     return base::PendingPollResult();                 \
221   auto var = std::move(assign_and_return_if_poll_##var.item())
222 
223 // Evaluates |expr|, which should return a PollResult. If IsPending is
224 // true, returns base::PendingPollResult().
225 //
226 // Example usage:
227 //
228 // Strean<int> MyIntReturningStreamFn();
229 //
230 // StreamPollResult<std::string> Poll(PollContext* ctx) {
231 //   ASSIGN_OR_RETURN_IF_PENDING_STREAM(res, MyIntReturningStreamFn());
232 //   if (res.IsDone()) {
233 //     return DonePollResult();
234 //   }
235 //   return std::to_string(*foo);
236 // }
237 #define ASSIGN_OR_RETURN_IF_PENDING_STREAM(var, expr) \
238   auto var = (expr);                                  \
239   if (var.IsPending())                                \
240   return base::PendingPollResult()
241 
242 }  // namespace base
243 }  // namespace perfetto
244 
245 #endif  // INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_
246