proxygen
BlockingWaitTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
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 #include <folly/Portability.h>
17 
18 #if FOLLY_HAS_COROUTINES
19 
20 #include <folly/Optional.h>
21 #include <folly/ScopeGuard.h>
28 
29 #include <memory>
30 #include <type_traits>
31 
32 static_assert(
33  std::is_same<
35  std::declval<folly::coro::AwaitableReady<void>>())),
36  void>::value,
37  "");
38 static_assert(
39  std::is_same<
41  std::declval<folly::coro::AwaitableReady<int>>())),
42  int>::value,
43  "");
44 static_assert(
45  std::is_same<
47  std::declval<folly::coro::AwaitableReady<int&>>())),
48  int&>::value,
49  "");
50 static_assert(
51  std::is_same<
53  std::declval<folly::coro::AwaitableReady<int&&>>())),
54  int>::value,
55  "blockingWait() should convert rvalue-reference-returning awaitables "
56  "into a returned prvalue to avoid potential lifetime issues since "
57  "its possible the rvalue reference could have been to some temporary "
58  "object stored inside the Awaiter which would have been destructed "
59  "by the time blockingWait returns.");
60 
61 TEST(BlockingWait, SynchronousCompletionVoidResult) {
63 }
64 
65 TEST(BlockingWait, SynchronousCompletionPRValueResult) {
66  EXPECT_EQ(
68  EXPECT_EQ(
69  "hello",
72 }
73 
74 TEST(BlockingWait, SynchronousCompletionLValueResult) {
75  int value = 123;
76  int& result =
78  EXPECT_EQ(&value, &result);
79  EXPECT_EQ(123, result);
80 }
81 
82 TEST(BlockingWait, SynchronousCompletionRValueResult) {
83  auto p = std::make_unique<int>(123);
84  auto* ptr = p.get();
85 
86  // Should return a prvalue which will lifetime-extend when assigned to an
87  // auto&& local variable.
88  auto&& result = folly::coro::blockingWait(
89  folly::coro::AwaitableReady<std::unique_ptr<int>&&>{std::move(p)});
90 
91  EXPECT_EQ(ptr, result.get());
92  EXPECT_FALSE(p);
93 }
94 
95 struct TrickyAwaitable {
96  struct Awaiter {
97  std::unique_ptr<int> value_;
98 
99  bool await_ready() const {
100  return false;
101  }
102 
103  bool await_suspend(std::experimental::coroutine_handle<>) {
104  value_ = std::make_unique<int>(42);
105  return false;
106  }
107 
108  std::unique_ptr<int>&& await_resume() {
109  return std::move(value_);
110  }
111  };
112 
113  Awaiter operator co_await() {
114  return {};
115  }
116 };
117 
118 TEST(BlockingWait, ReturnRvalueReferenceFromAwaiter) {
119  // This awaitable stores the result in the temporary Awaiter object that
120  // is placed on the coroutine frame as part of the co_await expression.
121  // It then returns an rvalue-reference to the value inside this temporary
122  // Awaiter object. This test is making sure that we copy/move the result
123  // before destructing the Awaiter object.
124  auto result = folly::coro::blockingWait(TrickyAwaitable{});
125  CHECK(result);
126  CHECK_EQ(42, *result);
127 }
128 
129 TEST(BlockingWait, AsynchronousCompletionOnAnotherThread) {
130  folly::coro::Baton baton;
131  std::thread t{[&] { baton.post(); }};
132  SCOPE_EXIT {
133  t.join();
134  };
136 }
137 
138 template <typename T>
139 class SimplePromise {
140  public:
141  class WaitOperation {
142  public:
143  explicit WaitOperation(
144  folly::coro::Baton& baton,
146  : awaiter_(baton), value_(value) {}
147 
148  bool await_ready() {
149  return awaiter_.await_ready();
150  }
151 
152  template <typename Promise>
153  auto await_suspend(std::experimental::coroutine_handle<Promise> h) {
154  return awaiter_.await_suspend(h);
155  }
156 
157  T&& await_resume() {
158  awaiter_.await_resume();
159  return std::move(*value_);
160  }
161 
162  private:
164  folly::Optional<T>& value_;
165  };
166 
167  SimplePromise() = default;
168 
169  WaitOperation operator co_await() {
170  return WaitOperation{baton_, value_};
171  }
172 
173  template <typename... Args>
174  void emplace(Args&&... args) {
175  value_.emplace(static_cast<Args&&>(args)...);
176  baton_.post();
177  }
178 
179  private:
180  folly::coro::Baton baton_;
181  folly::Optional<T> value_;
182 };
183 
184 TEST(BlockingWait, WaitOnSimpleAsyncPromise) {
185  SimplePromise<std::string> p;
186  std::thread t{[&] { p.emplace("hello coroutines!"); }};
187  SCOPE_EXIT {
188  t.join();
189  };
190  auto result = folly::coro::blockingWait(p);
191  EXPECT_EQ("hello coroutines!", result);
192 }
193 
194 struct MoveCounting {
195  int count_;
196  MoveCounting() noexcept : count_(0) {}
197  MoveCounting(MoveCounting&& other) noexcept : count_(other.count_ + 1) {}
198  MoveCounting& operator=(MoveCounting&& other) = delete;
199 };
200 
201 TEST(BlockingWait, WaitOnMoveOnlyAsyncPromise) {
202  SimplePromise<MoveCounting> p;
203  std::thread t{[&] { p.emplace(); }};
204  SCOPE_EXIT {
205  t.join();
206  };
207  auto result = folly::coro::blockingWait(p);
208 
209  // Number of move-constructions:
210  // 0. Value is in-place constructed in Optional<T>
211  // 0. await_resume() returns rvalue reference to Optional<T> value.
212  // 1. return_value() moves value into Try<T>
213  // 2. Value is moved from Try<T> to blockingWait() return value.
214  EXPECT_GE(2, result.count_);
215 }
216 
217 TEST(BlockingWait, moveCountingAwaitableReady) {
218  folly::coro::AwaitableReady<MoveCounting> awaitable{MoveCounting{}};
219  auto result = folly::coro::blockingWait(awaitable);
220 
221  // Moves:
222  // 1. Move value into AwaitableReady
223  // 2. Move value to await_resume() return-value
224  // 3. Move value to Try<T>
225  // 4. Move value to blockingWait() return-value
226  EXPECT_GE(4, result.count_);
227 }
228 
229 TEST(BlockingWait, WaitInFiber) {
230  SimplePromise<int> promise;
231  folly::EventBase evb;
232  auto& fm = folly::fibers::getFiberManager(evb);
233 
234  auto future =
235  fm.addTaskFuture([&] { return folly::coro::blockingWait(promise); });
236 
237  evb.loopOnce();
238  EXPECT_FALSE(future.isReady());
239 
240  promise.emplace(42);
241 
242  evb.loopOnce();
243  EXPECT_TRUE(future.isReady());
244  EXPECT_EQ(42, std::move(future).get());
245 }
246 
247 #endif
void * ptr
#define T(v)
Definition: http_parser.c:233
*than *hazptr_holder h
Definition: Hazptr.h:116
#define TEST(test_case_name, test_name)
Definition: gtest.h:2187
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
requires E e noexcept(noexcept(s.error(std::move(e))))
void post() noexcept
#define EXPECT_GE(val1, val2)
Definition: gtest.h:1932
bool loopOnce(int flags=0)
Definition: EventBase.cpp:271
static const char *const value
Definition: Conv.cpp:50
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
auto blockingWait(Awaitable &&awaitable) -> detail::decay_rvalue_reference_t< await_result_t< Awaitable >>
Definition: BlockingWait.h:313
FiberManager & getFiberManager(EventBase &evb, const FiberManager::Options &opts)