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() {
}
template <class T>
-Future<void> Future<T>::then() {
+Future<Unit> Future<T>::then() {
return then([] () {});
}
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);
}
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>
}
// via
-Future<void> via(Executor* executor, int8_t priority) {
+Future<Unit> via(Executor* executor, int8_t priority) {
return makeFuture().via(executor, priority);
}
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>
}
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()));
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));
});
return std::move(wait().value());
}
-template <>
-inline void Future<void>::get() {
- wait().value();
-}
-
template <class T>
T Future<T>::get(Duration dur) {
wait(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 {
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>
}
// 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>;
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.
template <typename T>
struct isFuture : std::false_type {
- typedef T Inner;
+ using Inner = typename Unit::Lift<T>::type;
};
template <typename T>
template <bool isTry, typename F, typename... Args>
struct argResult {
- typedef resultOf<F, Args...> Result;
+ using Result = resultOf<F, Args...>;
};
template<typename F, typename... Args>
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())> { };
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>;
namespace folly { namespace futures {
-Future<void> sleep(Duration dur, Timekeeper* tk) {
+Future<Unit> sleep(Duration dur, Timekeeper* tk) {
if (LIKELY(!tk)) {
tk = detail::getTimekeeperSingleton();
}
}
}}
-
-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
-}
-
-}}
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();
-> 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
/// 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
/// 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
#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
/// 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.
/// 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.
///
/// 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
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) {
}
}
+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) {
* 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 {
*/
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
*
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
*
/// 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 {
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) {
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([=]{
/// 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_;
/// 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
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.
*
* @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);
// 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) {
// 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){
// ...
// 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) {
// 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){
// ...
// 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();
});
// 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());
// 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());
// 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());
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());
});
}
}
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());
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)
/// 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());
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());
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);
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>));
}
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();
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);
.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();
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);
}
makeFuture()
.then([]{ return makeFuture(); })
- .then([&](Try<void>&& t) { flag = true; });
+ .then([&](Try<Unit>&& t) { flag = true; });
EXPECT_TRUE(flag); flag = false;
}
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) {
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());
});
}
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);
}
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());
}
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) {
p1.setValue(3);
p2.setValue(4);
}
+
+TEST(Future, makeFutureNoThrow) {
+ makeFuture().value();
+}
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));
});
}
TEST(Interrupt, cancel) {
- Promise<void> p;
+ Promise<Unit> p;
p.setInterruptHandler([&](const exception_wrapper& e) {
EXPECT_THROW(e.throwException(), FutureCancellation);
});
}
TEST(Interrupt, interruptAfterFulfilNoop) {
- Promise<void> p;
+ Promise<Unit> p;
bool flag = false;
p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
p.setValue();
}
TEST(Interrupt, secondInterruptNoop) {
- Promise<void> p;
+ Promise<Unit> p;
int count = 0;
p.setInterruptHandler([&](const exception_wrapper& e) { count++; });
auto f = p.getFuture();
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;
});
}
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());
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());
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;
}
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);
}
});
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
}
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());
}
}
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);
}
}
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);
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) {
p.setValue(42);
EXPECT_TRUE(fu.isReady());
}
+
+TEST(Unit, makeFutureWith) {
+ int count = 0;
+ Future<Unit> fu = makeFutureWith([&]{ count++; });
+ EXPECT_EQ(1, count);
+}
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()
}
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);
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();
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([&] {
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();
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();
}
{
- 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;
{
// 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();
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();