proxygen
CallbackLifetimeTest.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 
17 #include <folly/futures/Future.h>
18 
19 #include <thread>
20 
23 
24 using namespace folly;
25 
26 namespace {
27 
28 /***
29  * The basic premise is to check that the callback passed to then or onError
30  * is destructed before wait returns on the resulting future.
31  *
32  * The approach is to use callbacks where the destructor sleeps 500ms and then
33  * mutates a counter allocated on the caller stack. The caller checks the
34  * counter immediately after calling wait. Were the callback not destructed
35  * before wait returns, then we would very likely see an unchanged counter just
36  * after wait returns. But if, as we expect, the callback were destructed
37  * before wait returns, then we must be guaranteed to see a mutated counter
38  * just after wait returns.
39  *
40  * Note that the failure condition is not strictly guaranteed under load. :(
41  */
42 class CallbackLifetimeTest : public testing::Test {
43  public:
44  using CounterPtr = std::unique_ptr<size_t>;
45 
46  static bool kRaiseWillThrow() {
47  return true;
48  }
49  static constexpr auto kDelay() {
50  return std::chrono::milliseconds(500);
51  }
52 
53  auto mkC() {
54  return std::make_unique<size_t>(0);
55  }
56  auto mkCGuard(CounterPtr& ptr) {
57  return makeGuard([&] {
58  /* sleep override */ std::this_thread::sleep_for(kDelay());
59  ++*ptr;
60  });
61  }
62 
63  static void raise(folly::Unit = folly::Unit{}) {
64  if (kRaiseWillThrow()) { // to avoid marking [[noreturn]]
65  throw std::runtime_error("raise");
66  }
67  }
68  static Future<Unit> raiseFut() {
69  raise();
70  return makeFuture();
71  }
72 
73  TestExecutor executor{2}; // need at least 2 threads for internal futures
74 };
75 } // namespace
76 
77 TEST_F(CallbackLifetimeTest, thenReturnsValue) {
78  auto c = mkC();
79  via(&executor).thenValue([_ = mkCGuard(c)](auto&&) {}).wait();
80  EXPECT_EQ(1, *c);
81 }
82 
83 TEST_F(CallbackLifetimeTest, thenReturnsValueThrows) {
84  auto c = mkC();
85  via(&executor).thenValue([_ = mkCGuard(c)](auto&&) { raise(); }).wait();
86  EXPECT_EQ(1, *c);
87 }
88 
89 TEST_F(CallbackLifetimeTest, thenReturnsFuture) {
90  auto c = mkC();
91  via(&executor)
92  .thenValue([_ = mkCGuard(c)](auto&&) { return makeFuture(); })
93  .wait();
94  EXPECT_EQ(1, *c);
95 }
96 
97 TEST_F(CallbackLifetimeTest, thenReturnsFutureThrows) {
98  auto c = mkC();
99  via(&executor)
100  .thenValue([_ = mkCGuard(c)](auto&&) { return raiseFut(); })
101  .wait();
102  EXPECT_EQ(1, *c);
103 }
104 
105 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatch) {
106  auto c = mkC();
107  via(&executor)
108  .thenValue(raise)
109  .onError([_ = mkCGuard(c)](std::exception&) {})
110  .wait();
111  EXPECT_EQ(1, *c);
112 }
113 
114 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatchThrows) {
115  auto c = mkC();
116  via(&executor)
117  .thenValue(raise)
118  .onError([_ = mkCGuard(c)](std::exception&) { raise(); })
119  .wait();
120  EXPECT_EQ(1, *c);
121 }
122 
123 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrong) {
124  auto c = mkC();
125  via(&executor)
126  .thenValue(raise)
127  .onError([_ = mkCGuard(c)](std::logic_error&) {})
128  .wait();
129  EXPECT_EQ(1, *c);
130 }
131 
132 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrongThrows) {
133  auto c = mkC();
134  via(&executor)
135  .thenValue(raise)
136  .onError([_ = mkCGuard(c)](std::logic_error&) { raise(); })
137  .wait();
138  EXPECT_EQ(1, *c);
139 }
140 
141 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatch) {
142  auto c = mkC();
143  via(&executor)
144  .thenValue(raise)
145  .onError([_ = mkCGuard(c)](std::exception&) { return makeFuture(); })
146  .wait();
147  EXPECT_EQ(1, *c);
148 }
149 
150 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatchThrows) {
151  auto c = mkC();
152  via(&executor)
153  .thenValue(raise)
154  .onError([_ = mkCGuard(c)](std::exception&) { return raiseFut(); })
155  .wait();
156  EXPECT_EQ(1, *c);
157 }
158 
159 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrong) {
160  auto c = mkC();
161  via(&executor)
162  .thenValue(raise)
163  .onError([_ = mkCGuard(c)](std::logic_error&) { return makeFuture(); })
164  .wait();
165  EXPECT_EQ(1, *c);
166 }
167 
168 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrongThrows) {
169  auto c = mkC();
170  via(&executor)
171  .thenValue(raise)
172  .onError([_ = mkCGuard(c)](std::logic_error&) { return raiseFut(); })
173  .wait();
174  EXPECT_EQ(1, *c);
175 }
176 
177 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValue) {
178  auto c = mkC();
179  via(&executor)
180  .thenValue(raise)
181  .onError([_ = mkCGuard(c)](exception_wrapper&&) {})
182  .wait();
183  EXPECT_EQ(1, *c);
184 }
185 
186 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValueThrows) {
187  auto c = mkC();
188  via(&executor)
189  .thenValue(raise)
190  .onError([_ = mkCGuard(c)](exception_wrapper&&) { raise(); })
191  .wait();
192  EXPECT_EQ(1, *c);
193 }
194 
195 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFuture) {
196  auto c = mkC();
197  via(&executor)
198  .thenValue(raise)
199  .onError([_ = mkCGuard(c)](exception_wrapper&&) { return makeFuture(); })
200  .wait();
201  EXPECT_EQ(1, *c);
202 }
203 
204 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFutureThrows) {
205  auto c = mkC();
206  via(&executor)
207  .thenValue(raise)
208  .onError([_ = mkCGuard(c)](exception_wrapper&&) { return raiseFut(); })
209  .wait();
210  EXPECT_EQ(1, *c);
211 }
void * ptr
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
PUSHMI_INLINE_VAR constexpr __adl::get_executor_fn executor
bool wait(Waiter *waiter, bool shouldSleep, Waiter *&next)
TEST_F(AsyncSSLSocketWriteTest, write_coalescing1)
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
auto via(Executor *x, Func &&func) -> Future< typename isFutureOrSemiFuture< decltype(std::declval< Func >()())>::Inner >
Definition: Future-inl.h:1290
const internal::AnythingMatcher _
char c
Future< typename std::decay< T >::type > makeFuture(T &&t)
Definition: Future-inl.h:1310