Commit e845ef57 authored by Hans Fugal's avatar Hans Fugal Committed by Sara Golemon

Nuke Future<void> (folly/futures)

Summary: Nuke Future<void> in favor of Future<Unit>, for the `folly/futures` subdirectory.

Reviewed By: @djwatson

Differential Revision: D2201259
parent 489ab051
......@@ -51,7 +51,7 @@ Future<T>::Future(T2&& val)
template <class T>
template <typename, typename>
Future<T>::Future()
: core_(new detail::Core<T>(Try<T>())) {}
: core_(new detail::Core<T>(Try<T>(T()))) {}
template <class T>
Future<T>::~Future() {
......@@ -226,7 +226,7 @@ auto Future<T>::then(Executor* x, Arg&& arg, Args&&... args)
}
template <class T>
Future<void> Future<T>::then() {
Future<Unit> Future<T>::then() {
return then([] () {});
}
......@@ -432,8 +432,6 @@ inline Future<T> Future<T>::via(Executor* executor, int8_t priority) & {
template <class Func>
auto via(Executor* x, Func func)
-> Future<typename isFuture<decltype(func())>::Inner>
// this would work, if not for Future<void> :-/
// -> decltype(via(x).then(func))
{
// TODO make this actually more performant. :-P #7260175
return via(x).then(func);
......@@ -468,24 +466,29 @@ Future<typename std::decay<T>::type> makeFuture(T&& t) {
}
inline // for multiple translation units
Future<void> makeFuture() {
return makeFuture(Try<void>());
Future<Unit> makeFuture() {
return makeFuture(Unit{});
}
// XXX why is the dual necessary here? Can't we just use perfect forwarding
// and capture func by reference always?
template <class F>
auto makeFutureWith(
F&& func,
typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
-> Future<decltype(func())> {
return makeFuture(makeTryWith([&func]() {
-> Future<typename Unit::Lift<decltype(func())>::type> {
using LiftedResult = typename Unit::Lift<decltype(func())>::type;
return makeFuture<LiftedResult>(makeTryWith([&func]() {
return (func)();
}));
}
template <class F>
auto makeFutureWith(F const& func) -> Future<decltype(func())> {
auto makeFutureWith(F const& func)
-> Future<typename Unit::Lift<decltype(func())>::type> {
F copy = func;
return makeFuture(makeTryWith(std::move(copy)));
using LiftedResult = typename Unit::Lift<decltype(func())>::type;
return makeFuture<LiftedResult>(makeTryWith(std::move(copy)));
}
template <class T>
......@@ -511,7 +514,7 @@ Future<T> makeFuture(Try<T>&& t) {
}
// via
Future<void> via(Executor* executor, int8_t priority) {
Future<Unit> via(Executor* executor, int8_t priority) {
return makeFuture().via(executor, priority);
}
......@@ -603,14 +606,6 @@ struct CollectContext {
std::atomic<bool> threw {false};
};
// Specialize for void (implementations in Future.cpp)
template <>
CollectContext<void>::~CollectContext();
template <>
void CollectContext<void>::setPartialResult(size_t i, Try<void>& t);
}
template <class InputIterator>
......@@ -884,7 +879,7 @@ Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
}
tk->after(dur)
.then([ctx](Try<void> const& t) {
.then([ctx](Try<Unit> const& t) {
if (ctx->token.exchange(true) == false) {
if (t.hasException()) {
ctx->promise.setException(std::move(t.exception()));
......@@ -908,7 +903,7 @@ Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
template <class T>
Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
return collectAll(*this, futures::sleep(dur, tk))
.then([](std::tuple<Try<T>, Try<void>> tup) {
.then([](std::tuple<Try<T>, Try<Unit>> tup) {
Try<T>& t = std::get<0>(tup);
return makeFuture<T>(std::move(t));
});
......@@ -1007,11 +1002,6 @@ T Future<T>::get() {
return std::move(wait().value());
}
template <>
inline void Future<void>::get() {
wait().value();
}
template <class T>
T Future<T>::get(Duration dur) {
wait(dur);
......@@ -1022,26 +1012,11 @@ T Future<T>::get(Duration dur) {
}
}
template <>
inline void Future<void>::get(Duration dur) {
wait(dur);
if (isReady()) {
return;
} else {
throw TimedOut();
}
}
template <class T>
T Future<T>::getVia(DrivableExecutor* e) {
return std::move(waitVia(e).value());
}
template <>
inline void Future<void>::getVia(DrivableExecutor* e) {
waitVia(e).value();
}
namespace detail {
template <class T>
struct TryEquals {
......@@ -1049,13 +1024,6 @@ namespace detail {
return t1.value() == t2.value();
}
};
template <>
struct TryEquals<void> {
static bool equals(const Try<void>& t1, const Try<void>& t2) {
return true;
}
};
}
template <class T>
......@@ -1134,7 +1102,7 @@ namespace futures {
}
// Instantiate the most common Future types to save compile time
extern template class Future<void>;
extern template class Future<Unit>;
extern template class Future<bool>;
extern template class Future<int>;
extern template class Future<int64_t>;
......@@ -1142,8 +1110,3 @@ extern template class Future<std::string>;
extern template class Future<double>;
} // namespace folly
// I haven't included a Future<T&> specialization because I don't forsee us
// using it, however it is not difficult to add when needed. Refer to
// Future<void> for guidance. std::future and boost::future code would also be
// instructive.
......@@ -24,7 +24,7 @@ template <class> class Promise;
template <typename T>
struct isFuture : std::false_type {
typedef T Inner;
using Inner = typename Unit::Lift<T>::type;
};
template <typename T>
......@@ -63,7 +63,7 @@ struct ArgType<> {
template <bool isTry, typename F, typename... Args>
struct argResult {
typedef resultOf<F, Args...> Result;
using Result = resultOf<F, Args...>;
};
template<typename F, typename... Args>
......@@ -100,19 +100,6 @@ struct callableResult {
typedef Future<typename ReturnsFuture::Inner> Return;
};
template<typename F>
struct callableResult<void, F> {
typedef typename std::conditional<
callableWith<F>::value,
detail::argResult<false, F>,
typename std::conditional<
callableWith<F, Try<void>&&>::value,
detail::argResult<true, F, Try<void>&&>,
detail::argResult<true, F, Try<void>&>>::type>::type Arg;
typedef isFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
template <typename L>
struct Extract : Extract<decltype(&L::operator())> { };
......
......@@ -20,7 +20,7 @@
namespace folly {
// Instantiate the most common Future types to save compile time
template class Future<void>;
template class Future<Unit>;
template class Future<bool>;
template class Future<int>;
template class Future<int64_t>;
......@@ -31,7 +31,7 @@ template class Future<double>;
namespace folly { namespace futures {
Future<void> sleep(Duration dur, Timekeeper* tk) {
Future<Unit> sleep(Duration dur, Timekeeper* tk) {
if (LIKELY(!tk)) {
tk = detail::getTimekeeperSingleton();
}
......@@ -39,19 +39,3 @@ Future<void> sleep(Duration dur, Timekeeper* tk) {
}
}}
namespace folly { namespace detail {
template <>
CollectContext<void>::~CollectContext() {
if (!threw.exchange(true)) {
p.setValue();
}
}
template <>
void CollectContext<void>::setPartialResult(size_t i, Try<void>& t) {
// Nothing to do for void
}
}}
......@@ -62,7 +62,7 @@ class Future {
template <class T2 = T, typename =
typename std::enable_if<
folly::is_void_or_unit<T2>::value>::type>
std::is_same<Unit, T2>::value>::type>
Future();
~Future();
......@@ -202,9 +202,9 @@ class Future {
-> decltype(this->then(std::forward<Arg>(arg),
std::forward<Args>(args)...));
/// Convenience method for ignoring the value and creating a Future<void>.
/// Convenience method for ignoring the value and creating a Future<Unit>.
/// Exceptions still propagate.
Future<void> then();
Future<Unit> then();
/// Set an error callback for this Future. The callback should take a single
/// argument of the type that you want to catch, and should return a value of
......
......@@ -70,13 +70,6 @@ public:
/// handled.
void setInterruptHandler(std::function<void(exception_wrapper const&)>);
/// Fulfill this Promise<void>
template <class B = T>
typename std::enable_if<std::is_void<B>::value, void>::type
setValue() {
setTry(Try<T>());
}
/// Sugar to fulfill this Promise<Unit>
template <class B = T>
typename std::enable_if<std::is_same<Unit, B>::value, void>::type
......
......@@ -81,13 +81,6 @@ public:
/// handled.
void setInterruptHandler(std::function<void(exception_wrapper const&)>);
/// Fulfill this SharedPromise<void>
template <class B = T>
typename std::enable_if<std::is_void<B>::value, void>::type
setValue() {
setTry(Try<T>());
}
/// Sugar to fulfill this SharedPromise<Unit>
template <class B = T>
typename std::enable_if<std::is_same<Unit, B>::value, void>::type
......
......@@ -17,13 +17,14 @@
#pragma once
#include <folly/futures/detail/Types.h>
#include <folly/futures/Unit.h>
namespace folly {
template <class> class Future;
/// A Timekeeper handles the details of keeping time and fulfilling delay
/// promises. The returned Future<void> will either complete after the
/// promises. The returned Future<Unit> will either complete after the
/// elapsed time, or in the event of some kind of exceptional error may hold
/// an exception. These Futures respond to cancellation. If you use a lot of
/// Delays and many of them ultimately are unneeded (as would be the case for
......@@ -34,7 +35,7 @@ template <class> class Future;
/// use them implicitly behind the scenes by passing a timeout to some Future
/// operation.
///
/// Although we don't formally alias Delay = Future<void>,
/// Although we don't formally alias Delay = Future<Unit>,
/// that's an appropriate term for it. People will probably also call these
/// Timeouts, and that's ok I guess, but that term is so overloaded I thought
/// it made sense to introduce a cleaner term.
......@@ -54,7 +55,7 @@ class Timekeeper {
/// This future probably completes on the timer thread. You should almost
/// certainly follow it with a via() call or the accuracy of other timers
/// will suffer.
virtual Future<void> after(Duration) = 0;
virtual Future<Unit> after(Duration) = 0;
/// Returns a future that will complete at the requested time.
///
......@@ -65,7 +66,7 @@ class Timekeeper {
/// the system clock but rather execute that many milliseconds in the future
/// according to the steady clock.
template <class Clock>
Future<void> at(std::chrono::time_point<Clock> when);
Future<Unit> at(std::chrono::time_point<Clock> when);
};
} // namespace folly
......@@ -77,7 +78,7 @@ class Timekeeper {
namespace folly {
template <class Clock>
Future<void> Timekeeper::at(std::chrono::time_point<Clock> when) {
Future<Unit> Timekeeper::at(std::chrono::time_point<Clock> when) {
auto now = Clock::now();
if (when <= now) {
......
......@@ -31,6 +31,21 @@ Try<T>::Try(Try<T>&& t) noexcept : contains_(t.contains_) {
}
}
template <class T>
template <class T2>
Try<T>::Try(typename std::enable_if<std::is_same<Unit, T2>::value,
Try<void> const&>::type t)
: contains_(Contains::NOTHING) {
if (t.hasValue()) {
contains_ = Contains::VALUE;
new (&value_) T();
} else if (t.hasException()) {
contains_ = Contains::EXCEPTION;
new (&e_) std::unique_ptr<exception_wrapper>(
folly::make_unique<exception_wrapper>(t.exception()));
}
}
template <class T>
Try<T>& Try<T>::operator=(Try<T>&& t) noexcept {
if (this == &t) {
......
......@@ -35,8 +35,7 @@ namespace folly {
* context. Exceptions are stored as exception_wrappers so that the user can
* minimize rethrows if so desired.
*
* There is a specialization, Try<void>, which represents either success
* or an exception.
* To represent success or a captured exception, use Try<Unit>
*/
template <class T>
class Try {
......@@ -74,6 +73,12 @@ class Try {
*/
explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
/// Implicit conversion from Try<void> to Try<Unit>
template <class T2 = T>
/* implicit */
Try(typename std::enable_if<std::is_same<Unit, T2>::value,
Try<void> const&>::type t);
/*
* Construct a Try with an exception_wrapper
*
......@@ -364,7 +369,7 @@ typename std::enable_if<
makeTryWith(F&& f);
/*
* Specialization of makeTryWith for void
* Specialization of makeTryWith for void return
*
* @param f a function to execute and capture the result of
*
......
......@@ -22,8 +22,6 @@ namespace folly {
/// metaprogramming. So, instead of e.g. Future<void>, we have Future<Unit>.
/// You can ignore the actual value, and we port some of the syntactic
/// niceties like setValue() instead of setValue(Unit{}).
// We will soon return Future<Unit> wherever we currently return Future<void>
// #6847876
struct Unit {
/// Lift type T into Unit. This is the definition for all non-void types.
template <class T> struct Lift : public std::false_type {
......
......@@ -32,13 +32,13 @@ namespace {
return new WTCallback(base);
}
Future<void> getFuture() {
Future<Unit> getFuture() {
return promise_.getFuture();
}
protected:
EventBase* base_;
Promise<void> promise_;
Promise<Unit> promise_;
explicit WTCallback(EventBase* base)
: base_(base) {
......@@ -81,7 +81,7 @@ ThreadWheelTimekeeper::~ThreadWheelTimekeeper() {
thread_.join();
}
Future<void> ThreadWheelTimekeeper::after(Duration dur) {
Future<Unit> ThreadWheelTimekeeper::after(Duration dur) {
auto cob = WTCallback::create(&eventBase_);
auto f = cob->getFuture();
eventBase_.runInEventBaseThread([=]{
......
......@@ -37,7 +37,7 @@ class ThreadWheelTimekeeper : public Timekeeper {
/// This future *does* complete on the timer thread. You should almost
/// certainly follow it with a via() call or the accuracy of other timers
/// will suffer.
Future<void> after(Duration) override;
Future<Unit> after(Duration) override;
protected:
folly::EventBase eventBase_;
......
......@@ -37,7 +37,7 @@ namespace futures {
/// The Timekeeper thread will be lazily created the first time it is
/// needed. If your program never uses any timeouts or other time-based
/// Futures you will pay no Timekeeper thread overhead.
Future<void> sleep(Duration, Timekeeper* = nullptr);
Future<Unit> sleep(Duration, Timekeeper* = nullptr);
/**
* Set func as the callback for each input Future and return a vector of
......@@ -72,21 +72,19 @@ template <class T>
Future<typename std::decay<T>::type> makeFuture(T&& t);
/** Make a completed void Future. */
Future<void> makeFuture();
Future<Unit> makeFuture();
/** Make a completed Future by executing a function. If the function throws
we capture the exception, otherwise we capture the result. */
template <class F>
auto makeFutureWith(
F&& func,
typename std::enable_if<
!std::is_reference<F>::value, bool>::type sdf = false)
-> Future<decltype(func())>;
F&& func,
typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
-> Future<typename Unit::Lift<decltype(func())>::type>;
template <class F>
auto makeFutureWith(
F const& func)
-> Future<decltype(func())>;
auto makeFutureWith(F const& func)
-> Future<typename Unit::Lift<decltype(func())>::type>;
/// Make a failed Future from an exception_ptr.
/// Because the Future's type cannot be inferred you have to specify it, e.g.
......@@ -120,7 +118,7 @@ Future<T> makeFuture(Try<T>&& t);
*
* @returns a void Future that will call back on the given executor
*/
inline Future<void> via(
inline Future<Unit> via(
Executor* executor,
int8_t priority = Executor::MID_PRI);
......
......@@ -170,8 +170,8 @@ BENCHMARK_DRAW_LINE();
// The old way. Throw an exception, and rethrow to access it upstream.
void throwAndCatchImpl() {
makeFuture()
.then([](Try<void>&&){ throw std::runtime_error("oh no"); })
.then([](Try<void>&& t) {
.then([](Try<Unit>&&){ throw std::runtime_error("oh no"); })
.then([](Try<Unit>&& t) {
try {
t.value();
} catch(const std::runtime_error& e) {
......@@ -190,8 +190,8 @@ void throwAndCatchImpl() {
// will try to wrap, so no exception_ptrs/rethrows are necessary.
void throwAndCatchWrappedImpl() {
makeFuture()
.then([](Try<void>&&){ throw std::runtime_error("oh no"); })
.then([](Try<void>&& t) {
.then([](Try<Unit>&&){ throw std::runtime_error("oh no"); })
.then([](Try<Unit>&& t) {
auto caught = t.withException<std::runtime_error>(
[](const std::runtime_error& e){
// ...
......@@ -203,10 +203,10 @@ void throwAndCatchWrappedImpl() {
// Better. Wrap an exception, and rethrow to access it upstream.
void throwWrappedAndCatchImpl() {
makeFuture()
.then([](Try<void>&&){
return makeFuture<void>(std::runtime_error("oh no"));
.then([](Try<Unit>&&){
return makeFuture<Unit>(std::runtime_error("oh no"));
})
.then([](Try<void>&& t) {
.then([](Try<Unit>&& t) {
try {
t.value();
} catch(const std::runtime_error& e) {
......@@ -220,10 +220,10 @@ void throwWrappedAndCatchImpl() {
// The new way. Wrap an exception, and access it via the wrapper upstream
void throwWrappedAndCatchWrappedImpl() {
makeFuture()
.then([](Try<void>&&){
return makeFuture<void>(std::runtime_error("oh no"));
.then([](Try<Unit>&&){
return makeFuture<Unit>(std::runtime_error("oh no"));
})
.then([](Try<void>&& t){
.then([](Try<Unit>&& t){
auto caught = t.withException<std::runtime_error>(
[](const std::runtime_error& e){
// ...
......
......@@ -87,14 +87,14 @@ TEST(Collect, collectAll) {
// check that futures are ready in then()
{
std::vector<Promise<void>> promises(10);
std::vector<Future<void>> futures;
std::vector<Promise<Unit>> promises(10);
std::vector<Future<Unit>> futures;
for (auto& p : promises)
futures.push_back(p.getFuture());
auto allf = collectAll(futures)
.then([](Try<std::vector<Try<void>>>&& ts) {
.then([](Try<std::vector<Try<Unit>>>&& ts) {
for (auto& f : ts.value())
f.value();
});
......@@ -166,8 +166,8 @@ TEST(Collect, collect) {
// void futures success case
{
std::vector<Promise<void>> promises(10);
std::vector<Future<void>> futures;
std::vector<Promise<Unit>> promises(10);
std::vector<Future<Unit>> futures;
for (auto& p : promises)
futures.push_back(p.getFuture());
......@@ -185,8 +185,8 @@ TEST(Collect, collect) {
// void futures failure case
{
std::vector<Promise<void>> promises(10);
std::vector<Future<void>> futures;
std::vector<Promise<Unit>> promises(10);
std::vector<Future<Unit>> futures;
for (auto& p : promises)
futures.push_back(p.getFuture());
......@@ -294,8 +294,8 @@ TEST(Collect, collectAny) {
// error
{
std::vector<Promise<void>> promises(10);
std::vector<Future<void>> futures;
std::vector<Promise<Unit>> promises(10);
std::vector<Future<Unit>> futures;
for (auto& p : promises)
futures.push_back(p.getFuture());
......@@ -334,12 +334,12 @@ TEST(Collect, collectAny) {
TEST(Collect, alreadyCompleted) {
{
std::vector<Future<void>> fs;
std::vector<Future<Unit>> fs;
for (int i = 0; i < 10; i++)
fs.push_back(makeFuture());
collectAll(fs)
.then([&](std::vector<Try<void>> ts) {
.then([&](std::vector<Try<Unit>> ts) {
EXPECT_EQ(fs.size(), ts.size());
});
}
......@@ -484,8 +484,8 @@ TEST(Collect, allParallelWithError) {
}
TEST(Collect, collectN) {
std::vector<Promise<void>> promises(10);
std::vector<Future<void>> futures;
std::vector<Promise<Unit>> promises(10);
std::vector<Future<Unit>> futures;
for (auto& p : promises)
futures.push_back(p.getFuture());
......@@ -493,7 +493,7 @@ TEST(Collect, collectN) {
bool flag = false;
size_t n = 3;
collectN(futures, n)
.then([&](std::vector<std::pair<size_t, Try<void>>> v) {
.then([&](std::vector<std::pair<size_t, Try<Unit>>> v) {
flag = true;
EXPECT_EQ(n, v.size());
for (auto& tt : v)
......@@ -510,13 +510,13 @@ TEST(Collect, collectN) {
/// Ensure that we can compile collectAll/Any with folly::small_vector
TEST(Collect, smallVector) {
static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<void>),
static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<Unit>),
"Futures should not be trivially copyable");
static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<int>),
"Futures should not be trivially copyable");
{
folly::small_vector<Future<void>> futures;
folly::small_vector<Future<Unit>> futures;
for (int i = 0; i < 10; i++)
futures.push_back(makeFuture());
......@@ -524,7 +524,7 @@ TEST(Collect, smallVector) {
auto anyf = collectAny(futures);
}
{
folly::small_vector<Future<void>> futures;
folly::small_vector<Future<Unit>> futures;
for (int i = 0; i < 10; i++)
futures.push_back(makeFuture());
......
......@@ -40,7 +40,7 @@ TEST(Context, basic) {
std::unique_ptr<TestData>(new TestData(10)));
// Start a future
Promise<void> p;
Promise<Unit> p;
auto future = p.getFuture().then([&]{
// Check that the context followed the future
EXPECT_TRUE(RequestContext::get() != nullptr);
......
......@@ -24,5 +24,5 @@ using namespace folly;
TEST(Core, size) {
// If this number goes down, it's fine!
// If it goes up, please seek professional advice ;-)
EXPECT_EQ(192, sizeof(detail::Core<void>));
EXPECT_EQ(192, sizeof(detail::Core<Unit>));
}
......@@ -95,7 +95,7 @@ TEST(ManualExecutor, waitForDoesNotDeadlock) {
folly::Baton<> baton;
auto f = makeFuture()
.via(&east)
.then([](Try<void>){ return makeFuture(); })
.then([](Try<Unit>){ return makeFuture(); })
.via(&west);
std::thread t([&]{
baton.post();
......@@ -162,7 +162,7 @@ TEST(Executor, RunnablePtr) {
TEST(Executor, ThrowableThen) {
InlineExecutor x;
auto f = Future<void>().via(&x).then([](){
auto f = Future<Unit>().via(&x).then([](){
throw std::runtime_error("Faildog");
});
EXPECT_THROW(f.value(), std::exception);
......
......@@ -320,11 +320,11 @@ TEST(Future, thenTry) {
.then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
EXPECT_TRUE(flag); flag = false;
makeFuture().then([&](Try<void>&& t) { flag = true; t.value(); });
makeFuture().then([&](Try<Unit>&& t) { flag = true; t.value(); });
EXPECT_TRUE(flag); flag = false;
Promise<void> p;
auto f = p.getFuture().then([&](Try<void>&& t) { flag = true; });
Promise<Unit> p;
auto f = p.getFuture().then([&](Try<Unit>&& t) { flag = true; });
EXPECT_FALSE(flag);
EXPECT_FALSE(f.isReady());
p.setValue();
......@@ -353,7 +353,7 @@ TEST(Future, thenValue) {
auto f = makeFuture<int>(eggs).then([&](int i){});
EXPECT_THROW(f.value(), eggs_t);
f = makeFuture<void>(eggs).then([&]{});
f = makeFuture<Unit>(eggs).then([&]{});
EXPECT_THROW(f.value(), eggs_t);
}
......@@ -366,7 +366,7 @@ TEST(Future, thenValueFuture) {
makeFuture()
.then([]{ return makeFuture(); })
.then([&](Try<void>&& t) { flag = true; });
.then([&](Try<Unit>&& t) { flag = true; });
EXPECT_TRUE(flag); flag = false;
}
......@@ -505,7 +505,7 @@ TEST(Future, makeFuture) {
EXPECT_TYPE(makeFutureWith(failfun), Future<int>);
EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t);
EXPECT_TYPE(makeFuture(), Future<void>);
EXPECT_TYPE(makeFuture(), Future<Unit>);
}
TEST(Future, finish) {
......@@ -568,18 +568,18 @@ TEST(Future, unwrap) {
TEST(Future, throwCaughtInImmediateThen) {
// Neither of these should throw "Promise already satisfied"
makeFuture().then(
[=](Try<void>&&) -> int { throw std::exception(); });
[=](Try<Unit>&&) -> int { throw std::exception(); });
makeFuture().then(
[=](Try<void>&&) -> Future<int> { throw std::exception(); });
[=](Try<Unit>&&) -> Future<int> { throw std::exception(); });
}
TEST(Future, throwIfFailed) {
makeFuture<void>(eggs)
.then([=](Try<void>&& t) {
makeFuture<Unit>(eggs)
.then([=](Try<Unit>&& t) {
EXPECT_THROW(t.throwIfFailed(), eggs_t);
});
makeFuture()
.then([=](Try<void>&& t) {
.then([=](Try<Unit>&& t) {
EXPECT_NO_THROW(t.throwIfFailed());
});
......@@ -600,7 +600,7 @@ TEST(Future, getFutureAfterSetValue) {
}
TEST(Future, getFutureAfterSetException) {
Promise<void> p;
Promise<Unit> p;
p.setWith([]() -> void { throw std::logic_error("foo"); });
EXPECT_THROW(p.getFuture().value(), std::logic_error);
}
......@@ -655,7 +655,7 @@ TEST(Future, CircularDependencySharedPtrSelfReset) {
TEST(Future, Constructor) {
auto f1 = []() -> Future<int> { return Future<int>(3); }();
EXPECT_EQ(f1.value(), 3);
auto f2 = []() -> Future<void> { return Future<void>(); }();
auto f2 = []() -> Future<Unit> { return Future<Unit>(); }();
EXPECT_NO_THROW(f2.value());
}
......@@ -664,7 +664,7 @@ TEST(Future, ImplicitConstructor) {
EXPECT_EQ(f1.value(), 3);
// Unfortunately, the C++ standard does not allow the
// following implicit conversion to work:
//auto f2 = []() -> Future<void> { }();
//auto f2 = []() -> Future<Unit> { }();
}
TEST(Future, thenDynamic) {
......@@ -734,3 +734,7 @@ TEST(Future, RequestContext) {
p1.setValue(3);
p2.setValue(4);
}
TEST(Future, makeFutureNoThrow) {
makeFuture().value();
}
......@@ -23,7 +23,7 @@ using namespace folly;
TEST(Interrupt, raise) {
std::runtime_error eggs("eggs");
Promise<void> p;
Promise<Unit> p;
p.setInterruptHandler([&](const exception_wrapper& e) {
EXPECT_THROW(e.throwException(), decltype(eggs));
});
......@@ -31,7 +31,7 @@ TEST(Interrupt, raise) {
}
TEST(Interrupt, cancel) {
Promise<void> p;
Promise<Unit> p;
p.setInterruptHandler([&](const exception_wrapper& e) {
EXPECT_THROW(e.throwException(), FutureCancellation);
});
......@@ -55,7 +55,7 @@ TEST(Interrupt, interruptThenHandle) {
}
TEST(Interrupt, interruptAfterFulfilNoop) {
Promise<void> p;
Promise<Unit> p;
bool flag = false;
p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
p.setValue();
......@@ -64,7 +64,7 @@ TEST(Interrupt, interruptAfterFulfilNoop) {
}
TEST(Interrupt, secondInterruptNoop) {
Promise<void> p;
Promise<Unit> p;
int count = 0;
p.setInterruptHandler([&](const exception_wrapper& e) { count++; });
auto f = p.getFuture();
......
......@@ -31,7 +31,7 @@ TEST(Map, basic) {
fs.push_back(p3.getFuture());
int c = 0;
std::vector<Future<void>> fs2 = futures::map(fs, [&](int i){
std::vector<Future<Unit>> fs2 = futures::map(fs, [&](int i){
c += i;
});
......
......@@ -34,7 +34,7 @@ TEST(Poll, notReady) {
}
TEST(Poll, exception) {
Promise<void> p;
Promise<Unit> p;
auto f = p.getFuture();
p.setWith([] { throw std::runtime_error("Runtime"); });
EXPECT_TRUE(f.poll().value().hasException());
......
......@@ -70,7 +70,7 @@ TEST(Promise, setValue) {
unique_ptr<int> ptr = std::move(fmov.value());
EXPECT_EQ(42, *ptr);
Promise<void> v;
Promise<Unit> v;
auto fv = v.getFuture();
v.setValue();
EXPECT_TRUE(fv.isReady());
......@@ -78,13 +78,13 @@ TEST(Promise, setValue) {
TEST(Promise, setException) {
{
Promise<void> p;
Promise<Unit> p;
auto f = p.getFuture();
p.setException(eggs);
EXPECT_THROW(f.value(), eggs_t);
}
{
Promise<void> p;
Promise<Unit> p;
auto f = p.getFuture();
try {
throw eggs;
......
......@@ -123,7 +123,7 @@ TEST(Timekeeper, futureWithinVoidSpecialization) {
}
TEST(Timekeeper, futureWithinException) {
Promise<void> p;
Promise<Unit> p;
auto f = p.getFuture().within(awhile, std::runtime_error("expected"));
EXPECT_THROW(f.get(), std::runtime_error);
}
......@@ -150,7 +150,7 @@ TEST(Timekeeper, onTimeoutVoid) {
});
makeFuture().delayed(one_ms)
.onTimeout(Duration(0), [&]{
return makeFuture<void>(std::runtime_error("expected"));
return makeFuture<Unit>(std::runtime_error("expected"));
});
// just testing compilation here
}
......
......@@ -36,7 +36,7 @@ TEST(Try, basic) {
A a(5);
Try<A> t_a(std::move(a));
Try<void> t_void;
Try<Unit> t_void;
EXPECT_EQ(5, t_a.value().x());
}
......
......@@ -35,7 +35,7 @@ TEST(Unit, operatorNe) {
}
TEST(Unit, voidOrUnit) {
EXPECT_TRUE(is_void_or_unit<void>::value);
EXPECT_TRUE(is_void_or_unit<Unit>::value);
EXPECT_TRUE(is_void_or_unit<Unit>::value);
EXPECT_FALSE(is_void_or_unit<int>::value);
}
......@@ -53,7 +53,7 @@ TEST(Unit, liftInt) {
}
TEST(Unit, liftVoid) {
using Lifted = Unit::Lift<void>;
using Lifted = Unit::Lift<Unit>;
EXPECT_TRUE(Lifted::value);
auto v = std::is_same<Unit, Lifted::type>::value;
EXPECT_TRUE(v);
......@@ -68,7 +68,7 @@ TEST(Unit, futureToUnit) {
TEST(Unit, voidFutureToUnit) {
Future<Unit> fu = makeFuture().unit();
fu.value();
EXPECT_TRUE(makeFuture<void>(eggs).unit().hasException());
EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());
}
TEST(Unit, unitFutureToUnitIdentity) {
......@@ -84,3 +84,9 @@ TEST(Unit, toUnitWhileInProgress) {
p.setValue(42);
EXPECT_TRUE(fu.isReady());
}
TEST(Unit, makeFutureWith) {
int count = 0;
Future<Unit> fu = makeFutureWith([&]{ count++; });
EXPECT_EQ(1, count);
}
......@@ -124,7 +124,7 @@ TEST(Via, thenFunction) {
TEST_F(ViaFixture, threadHops) {
auto westThreadId = std::this_thread::get_id();
auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
auto f = via(eastExecutor.get()).then([=](Try<Unit>&& t) {
EXPECT_NE(std::this_thread::get_id(), westThreadId);
return makeFuture<int>(1);
}).via(westExecutor.get()
......@@ -283,7 +283,7 @@ TEST(Via, then2) {
}
TEST(Via, then2Variadic) {
struct Foo { bool a = false; void foo(Try<void>) { a = true; } };
struct Foo { bool a = false; void foo(Try<Unit>) { a = true; } };
Foo f;
ManualExecutor x;
makeFuture().then(&x, &Foo::foo, &f);
......@@ -345,14 +345,14 @@ TEST(Via, callbackRace) {
ThreadExecutor x;
auto fn = [&x]{
auto promises = std::make_shared<std::vector<Promise<void>>>(4);
std::vector<Future<void>> futures;
auto promises = std::make_shared<std::vector<Promise<Unit>>>(4);
std::vector<Future<Unit>> futures;
for (auto& p : *promises) {
futures.emplace_back(
p.getFuture()
.via(&x)
.then([](Try<void>&&){}));
.then([](Try<Unit>&&){}));
}
x.waitForStartup();
......@@ -424,16 +424,16 @@ TEST(Via, waitVia) {
TEST(Via, viaRaces) {
ManualExecutor x;
Promise<void> p;
Promise<Unit> p;
auto tid = std::this_thread::get_id();
bool done = false;
std::thread t1([&] {
p.getFuture()
.via(&x)
.then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
.then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
.then([&](Try<void>&&) { done = true; });
.then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
.then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
.then([&](Try<Unit>&&) { done = true; });
});
std::thread t2([&] {
......@@ -448,7 +448,7 @@ TEST(Via, viaRaces) {
TEST(ViaFunc, liftsVoid) {
ManualExecutor x;
int count = 0;
Future<void> f = via(&x, [&]{ count++; });
Future<Unit> f = via(&x, [&]{ count++; });
EXPECT_EQ(0, count);
x.run();
......
......@@ -34,7 +34,7 @@ TEST(Wait, waitImmediate) {
EXPECT_EQ(v.size(), done_v.size());
EXPECT_EQ(v, done_v);
vector<Future<void>> v_f;
vector<Future<Unit>> v_f;
v_f.push_back(makeFuture());
v_f.push_back(makeFuture());
auto done_v_f = collectAll(v_f).wait().value();
......@@ -169,7 +169,7 @@ TEST(Wait, waitWithDuration) {
}
{
Promise<void> p;
Promise<Unit> p;
auto start = std::chrono::steady_clock::now();
auto f = p.getFuture().wait(milliseconds(100));
auto elapsed = std::chrono::steady_clock::now() - start;
......@@ -182,7 +182,7 @@ TEST(Wait, waitWithDuration) {
{
// Try to trigger the race where the resultant Future is not yet complete
// even if we didn't hit the timeout, and make sure we deal with it properly
Promise<void> p;
Promise<Unit> p;
folly::Baton<> b;
auto t = std::thread([&]{
b.post();
......
......@@ -57,14 +57,14 @@ TEST(Window, basic) {
fn(input, 1, 0);
}
{
// int -> Future<void>
// int -> Future<Unit>
auto res = reduce(
window(
std::vector<int>({1, 2, 3}),
[](int i) { return makeFuture(); },
2),
0,
[](int sum, const Try<void>& b) {
[](int sum, const Try<Unit>& b) {
EXPECT_TRUE(b.hasValue());
return sum + 1;
}).get();
......
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