Commit d28b94ab authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot

Disable future unwrapping in SemiFuture::defer(Value/Error)

Summary: Future unwrapping in defer is broken for incomplete Futures and SemiFutures with deferred work.

Reviewed By: yfeldblum

Differential Revision: D8675535

fbshipit-source-id: 8deff362ea018cf4717354cd973756161c4249aa
parent 860b718e
......@@ -540,6 +540,7 @@ class DeferredExecutor final : public Executor {
}
void setExecutor(folly::Executor* executor) {
DCHECK(!dynamic_cast<DeferredExecutor*>(executor));
if (nestedExecutors_) {
for (auto nestedExecutor : *nestedExecutors_) {
nestedExecutor->setExecutor(executor);
......@@ -880,6 +881,9 @@ template <class T>
template <typename F>
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
SemiFuture<T>::defer(F&& func) && {
static_assert(
!futures::detail::tryCallableResult<T, F>::ReturnsFuture::value,
"defer does not support Future unwrapping");
DeferredExecutor* deferredExecutor = getDeferredExecutor();
if (!deferredExecutor) {
deferredExecutor = new DeferredExecutor();
......@@ -898,6 +902,9 @@ template <class T>
template <typename F>
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
SemiFuture<T>::deferValue(F&& func) && {
static_assert(
!futures::detail::valueCallableResult<T, F>::ReturnsFuture::value,
"deferValue does not support Future unwrapping");
return std::move(*this).defer([f = std::forward<F>(func)](
folly::Try<T>&& t) mutable {
return std::forward<F>(f)(
......@@ -910,13 +917,17 @@ SemiFuture<T>::deferValue(F&& func) && {
template <class T>
template <class ExceptionType, class F>
SemiFuture<T> SemiFuture<T>::deferError(F&& func) && {
static_assert(
!isFutureOrSemiFuture<decltype(
std::forward<F>(func)(std::declval<ExceptionType&>()))>::value,
"deferError does not support Future unwrapping");
return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T>&& t) mutable {
if (auto e = t.template tryGetExceptionObject<ExceptionType>()) {
return makeSemiFutureWith(
[&]() mutable { return std::forward<F>(func)(*e); });
return Try<T>(makeTryWith([&] { return std::forward<F>(func)(*e); }))
.value();
} else {
return makeSemiFuture<T>(std::move(t));
return std::move(*t);
}
});
}
......@@ -924,14 +935,19 @@ SemiFuture<T> SemiFuture<T>::deferError(F&& func) && {
template <class T>
template <class F>
SemiFuture<T> SemiFuture<T>::deferError(F&& func) && {
static_assert(
!isFutureOrSemiFuture<decltype(
std::forward<F>(func)(std::declval<exception_wrapper&>()))>::value,
"deferError does not support Future unwrapping");
return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T> t) mutable {
if (t.hasException()) {
return makeSemiFutureWith([&]() mutable {
return std::forward<F>(func)(std::move(t.exception()));
});
return Try<T>(makeTryWith([&] {
return std::forward<F>(func)(std::move(t.exception()));
}))
.value();
} else {
return makeSemiFuture<T>(std::move(t));
return std::move(*t);
}
});
}
......@@ -942,7 +958,7 @@ SemiFuture<T> SemiFuture<T>::delayed(Duration dur, Timekeeper* tk) && {
.toUnsafeFuture()
.then([](std::tuple<Try<T>, Try<Unit>> tup) {
Try<T>& t = std::get<0>(tup);
return makeFuture<T>(std::move(t));
return makeFuture<T>(std::move(*t));
});
}
......
......@@ -255,11 +255,11 @@ TEST(SemiFuture, hasPostconditionInvalid) {
}
namespace {
SemiFuture<int> onErrorHelperEggs(const eggs_t&) {
return makeSemiFuture(10);
int onErrorHelperEggs(const eggs_t&) {
return 10;
}
SemiFuture<int> onErrorHelperGeneric(const folly::exception_wrapper&) {
return makeSemiFuture(20);
int onErrorHelperGeneric(const folly::exception_wrapper&) {
return 20;
}
} // namespace
......@@ -800,17 +800,6 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<eggs_t>([&](eggs_t const& /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// By auto reference
{
auto f = makeSemiFuture()
......@@ -820,17 +809,6 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<eggs_t>([&](auto& /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// By value
{
auto f = makeSemiFuture()
......@@ -840,25 +818,11 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<eggs_t>([&](eggs_t /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// auto value
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<eggs_t>([&](auto /* e */) {
flag();
return makeSemiFuture();
});
.deferError<eggs_t>([&](auto /* e */) { flag(); });
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
......@@ -873,17 +837,6 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<std::exception>([&](auto const& /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// Non-exceptions
{
auto f = makeSemiFuture()
......@@ -893,17 +846,6 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw - 1; })
.deferError<int>([&](auto /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// Mutable lambda
{
auto f =
......@@ -914,17 +856,6 @@ TEST(SemiFuture, onError) {
EXPECT_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<eggs_t>([&](auto const& /* e */) mutable {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// Function pointer
{
auto f = makeSemiFuture()
......@@ -960,17 +891,6 @@ TEST(SemiFuture, onError) {
EXPECT_NO_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { return 42; })
.deferError<eggs_t>([&](auto const& /* e */) {
flag();
return makeSemiFuture<int>(-1);
});
EXPECT_EQ(42, std::move(f).get());
EXPECT_NO_FLAG();
}
// Catch different exception
{
auto f = makeSemiFuture()
......@@ -981,17 +901,6 @@ TEST(SemiFuture, onError) {
EXPECT_NO_FLAG();
}
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError<std::runtime_error>([&](auto const& /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_THROW(std::move(f).get(), eggs_t);
EXPECT_NO_FLAG();
}
// Returned value propagates
{
auto f = makeSemiFuture()
......@@ -1000,16 +909,6 @@ TEST(SemiFuture, onError) {
EXPECT_EQ(42, std::move(f).get());
}
// Returned future propagates
{
auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; })
.deferError<eggs_t>([&](auto const& /* e */) {
return makeSemiFuture<int>(42);
});
EXPECT_EQ(42, std::move(f).get());
}
// Throw in callback
{
auto f = makeSemiFuture()
......@@ -1018,38 +917,6 @@ TEST(SemiFuture, onError) {
EXPECT_THROW(std::move(f).get(), eggs_t);
}
{
auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; })
.deferError<eggs_t>(
[&](auto const& e) -> SemiFuture<int> { throw e; });
EXPECT_THROW(std::move(f).get(), eggs_t);
}
// exception_wrapper, return Future<T>
{
auto f = makeSemiFuture()
.defer([] { throw eggs; })
.deferError([&](exception_wrapper /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
}
// exception_wrapper, return Future<T> but throw
{
auto f = makeSemiFuture()
.defer([]() -> int { throw eggs; })
.deferError([&](exception_wrapper /* e */) -> SemiFuture<int> {
flag();
throw eggs;
});
EXPECT_THROW(std::move(f).get(), eggs_t);
EXPECT_FLAG();
}
// exception_wrapper, return T
{
auto f = makeSemiFuture()
......@@ -1080,7 +947,6 @@ TEST(SemiFuture, onError) {
.defer([] { throw eggs; })
.deferError([&](const exception_wrapper& /* e */) {
flag();
return makeSemiFuture();
});
EXPECT_NO_THROW(std::move(f).get());
EXPECT_FLAG();
......@@ -1094,15 +960,15 @@ TEST(SemiFuture, makePromiseContract) {
EXPECT_EQ(4, std::move(c.second).get());
}
TEST(SemiFuture, invokeCallbackReturningFutureWithOriginalCVRef) {
TEST(SemiFuture, invokeCallbackWithOriginalCVRef) {
struct Foo {
Future<int> operator()(int x) & {
int operator()(int x) & {
return x + 1;
}
Future<int> operator()(int x) const& {
int operator()(int x) const& {
return x + 2;
}
Future<int> operator()(int x) && {
int operator()(int x) && {
return x + 3;
}
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment