2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/futures/Future.h>
21 #include <folly/futures/test/TestExecutor.h>
22 #include <folly/portability/GTest.h>
24 using namespace folly;
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.
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.
40 * Note that the failure condition is not strictly guaranteed under load. :(
42 class CallbackLifetimeTest : public testing::Test {
44 using CounterPtr = std::unique_ptr<size_t>;
46 static bool kRaiseWillThrow() {
49 static constexpr auto kDelay() {
50 return std::chrono::milliseconds(500);
54 return std::make_unique<size_t>(0);
56 auto mkCGuard(CounterPtr& ptr) {
57 return makeGuard([&] {
58 /* sleep override */ std::this_thread::sleep_for(kDelay());
64 if (kRaiseWillThrow()) { // to avoid marking [[noreturn]]
65 throw std::runtime_error("raise");
68 static Future<Unit> raiseFut() {
73 TestExecutor executor{2}; // need at least 2 threads for internal futures
77 TEST_F(CallbackLifetimeTest, thenReturnsValue) {
79 via(&executor).then([_ = mkCGuard(c)]{}).wait();
83 TEST_F(CallbackLifetimeTest, thenReturnsValueThrows) {
85 via(&executor).then([_ = mkCGuard(c)] { raise(); }).wait();
89 TEST_F(CallbackLifetimeTest, thenReturnsFuture) {
91 via(&executor).then([_ = mkCGuard(c)] { return makeFuture(); }).wait();
95 TEST_F(CallbackLifetimeTest, thenReturnsFutureThrows) {
97 via(&executor).then([_ = mkCGuard(c)] { return raiseFut(); }).wait();
101 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatch) {
105 .onError([_ = mkCGuard(c)](std::exception&){})
110 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatchThrows) {
114 .onError([_ = mkCGuard(c)](std::exception&) { raise(); })
119 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrong) {
123 .onError([_ = mkCGuard(c)](std::logic_error&){})
128 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrongThrows) {
132 .onError([_ = mkCGuard(c)](std::logic_error&) { raise(); })
137 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatch) {
141 .onError([_ = mkCGuard(c)](std::exception&) { return makeFuture(); })
146 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatchThrows) {
150 .onError([_ = mkCGuard(c)](std::exception&) { return raiseFut(); })
155 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrong) {
159 .onError([_ = mkCGuard(c)](std::logic_error&) { return makeFuture(); })
164 TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrongThrows) {
168 .onError([_ = mkCGuard(c)](std::logic_error&) { return raiseFut(); })
173 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValue) {
177 .onError([_ = mkCGuard(c)](exception_wrapper &&){})
182 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValueThrows) {
186 .onError([_ = mkCGuard(c)](exception_wrapper &&) { raise(); })
191 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFuture) {
195 .onError([_ = mkCGuard(c)](exception_wrapper &&) { return makeFuture(); })
200 TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFutureThrows) {
204 .onError([_ = mkCGuard(c)](exception_wrapper &&) { return raiseFut(); })