return ctx->p.getFuture();
}
+template <typename T>
+Future<T>
+waitWithSemaphore(Future<T>&& f) {
+ Baton<> baton;
+ auto done = f.then([&](Try<T> &&t) {
+ baton.post();
+ return std::move(t.value());
+ });
+ baton.wait();
+ while (!done.isReady()) {
+ // There's a race here between the return here and the actual finishing of
+ // the future. f is completed, but the setup may not have finished on done
+ // after the baton has posted.
+ std::this_thread::yield();
+ }
+ return done;
+}
+
+template<>
+inline Future<void> waitWithSemaphore<void>(Future<void>&& f) {
+ Baton<> baton;
+ auto done = f.then([&](Try<void> &&t) {
+ baton.post();
+ t.value();
+ });
+ baton.wait();
+ while (!done.isReady()) {
+ // There's a race here between the return here and the actual finishing of
+ // the future. f is completed, but the setup may not have finished on done
+ // after the baton has posted.
+ std::this_thread::yield();
+ }
+ return done;
+}
+
+template <typename T, class Dur>
+Future<T>
+waitWithSemaphore(Future<T>&& f, Dur timeout) {
+ auto baton = std::make_shared<Baton<>>();
+ auto done = f.then([baton](Try<T> &&t) {
+ baton->post();
+ return std::move(t.value());
+ });
+ baton->timed_wait(std::chrono::system_clock::now() + timeout);
+ return done;
+}
+
+template <class Dur>
+Future<void>
+waitWithSemaphore(Future<void>&& f, Dur timeout) {
+ auto baton = std::make_shared<Baton<>>();
+ auto done = f.then([baton](Try<void> &&t) {
+ baton->post();
+ t.value();
+ });
+ baton->timed_wait(std::chrono::system_clock::now() + timeout);
+ return done;
+}
+
namespace {
template <class T>
void getWaitHelper(Future<T>* f) {
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
whenN(InputIterator first, InputIterator last, size_t n);
+/** Wait for the given future to complete on a semaphore. Returns a completed
+ * future containing the result.
+ *
+ * NB if the promise for the future would be fulfilled in the same thread that
+ * you call this, it will deadlock.
+ */
+template <class T>
+Future<T> waitWithSemaphore(Future<T>&& f);
+
+/** Wait for up to `timeout` for the given future to complete. Returns a future
+ * which may or may not be completed depending whether the given future
+ * completed in time
+ *
+ * Note: each call to this starts a (short-lived) thread and allocates memory.
+ */
+template <typename T, class Dur>
+Future<T> waitWithSemaphore(Future<T>&& f, Dur timeout);
+
} // folly
#include <folly/futures/Future-inl.h>
});
}
-TEST(Future, waitImmediate) {
- makeFuture().wait();
- auto done = makeFuture(42).wait().value();
+TEST(Future, waitWithSemaphoreImmediate) {
+ waitWithSemaphore(makeFuture());
+ auto done = waitWithSemaphore(makeFuture(42)).value();
EXPECT_EQ(42, done);
vector<int> v{1,2,3};
- auto done_v = makeFuture(v).wait().value();
+ auto done_v = waitWithSemaphore(makeFuture(v)).value();
EXPECT_EQ(v.size(), done_v.size());
EXPECT_EQ(v, done_v);
vector<Future<void>> v_f;
v_f.push_back(makeFuture());
v_f.push_back(makeFuture());
- auto done_v_f = whenAll(v_f.begin(), v_f.end()).wait().value();
+ auto done_v_f = waitWithSemaphore(whenAll(v_f.begin(), v_f.end())).value();
EXPECT_EQ(2, done_v_f.size());
vector<Future<bool>> v_fb;
v_fb.push_back(makeFuture(true));
v_fb.push_back(makeFuture(false));
auto fut = whenAll(v_fb.begin(), v_fb.end());
- auto done_v_fb = std::move(fut.wait().value());
+ auto done_v_fb = std::move(waitWithSemaphore(std::move(fut)).value());
EXPECT_EQ(2, done_v_fb.size());
}
-TEST(Future, wait) {
+TEST(Future, waitWithSemaphore) {
Promise<int> p;
Future<int> f = p.getFuture();
std::atomic<bool> flag{false};
return t.value();
});
flag = true;
- result.store(n.wait().value());
+ result.store(waitWithSemaphore(std::move(n)).value());
},
std::move(f)
);
EXPECT_EQ(result.load(), 42);
}
-TEST(Future, waitWithDuration) {
+TEST(Future, waitWithSemaphoreForTime) {
{
Promise<int> p;
Future<int> f = p.getFuture();
- auto t = f.wait(std::chrono::milliseconds(1));
+ auto t = waitWithSemaphore(std::move(f),
+ std::chrono::microseconds(1));
EXPECT_FALSE(t.isReady());
p.setValue(1);
EXPECT_TRUE(t.isReady());
Promise<int> p;
Future<int> f = p.getFuture();
p.setValue(1);
- auto t = f.wait(std::chrono::milliseconds(1));
+ auto t = waitWithSemaphore(std::move(f),
+ std::chrono::milliseconds(1));
EXPECT_TRUE(t.isReady());
}
{
v_fb.push_back(makeFuture(true));
v_fb.push_back(makeFuture(false));
auto f = whenAll(v_fb.begin(), v_fb.end());
- auto t = f.wait(std::chrono::milliseconds(1));
+ auto t = waitWithSemaphore(std::move(f),
+ std::chrono::milliseconds(1));
EXPECT_TRUE(t.isReady());
EXPECT_EQ(2, t.value().size());
}
v_fb.push_back(p1.getFuture());
v_fb.push_back(p2.getFuture());
auto f = whenAll(v_fb.begin(), v_fb.end());
- auto t = f.wait(std::chrono::milliseconds(1));
+ auto t = waitWithSemaphore(std::move(f),
+ std::chrono::milliseconds(1));
EXPECT_FALSE(t.isReady());
p1.setValue(true);
EXPECT_FALSE(t.isReady());
EXPECT_TRUE(t.isReady());
}
{
- auto t = makeFuture().wait(std::chrono::milliseconds(1));
+ auto t = waitWithSemaphore(makeFuture(),
+ std::chrono::milliseconds(1));
EXPECT_TRUE(t.isReady());
}
}
return whenAll(futures.begin(), futures.end());
};
- fn().wait();
+ waitWithSemaphore(fn());
}
// Test of handling of a circular dependency. It's never recommended