Commit db37af84 authored by Matt Dordal's avatar Matt Dordal Committed by Dave Watson

add waitWithSemaphore to folly::wangle

Summary:
It may be useful to wait for a future to finish. This adds a utility function
to do so, returning a completed future.

NB: While it doesn't matter which thread executes the `then`, there does need
to be two threads. If not, this will deadlock forever. I'm not sure if there's
a way to detect/prevent that.

Test Plan: added some unit tests.

Reviewed By: hans@fb.com

FB internal diff: D1319330
parent d7bda3ad
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#pragma once #pragma once
#include "detail.h" #include "detail.h"
#include <folly/LifoSem.h>
namespace folly { namespace wangle { namespace folly { namespace wangle {
...@@ -418,6 +419,18 @@ whenN(InputIterator first, InputIterator last, size_t n) { ...@@ -418,6 +419,18 @@ whenN(InputIterator first, InputIterator last, size_t n) {
return ctx->p.getFuture(); return ctx->p.getFuture();
} }
template <typename F>
typename F::value_type waitWithSemaphore(F&& f) {
LifoSem sem;
Try<typename F::value_type> done;
f.then([&](Try<typename F::value_type> &&t) {
done = std::move(t);
sem.post();
});
sem.wait();
return done.value();
}
}} }}
// I haven't included a Future<T&> specialization because I don't forsee us // I haven't included a Future<T&> specialization because I don't forsee us
......
...@@ -313,6 +313,15 @@ Future<std::vector<std::pair< ...@@ -313,6 +313,15 @@ Future<std::vector<std::pair<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
whenN(InputIterator first, InputIterator last, size_t n); whenN(InputIterator first, InputIterator last, size_t n);
/** Wait for the given future to complete on a semaphore. Returns the result of
* the given future.
*
* NB if the promise for the future would be fulfilled in the same thread that
* you call this, it will deadlock.
*/
template <class F>
typename F::value_type waitWithSemaphore(F&& f);
}} // folly::wangle }} // folly::wangle
#include "Future-inl.h" #include "Future-inl.h"
...@@ -15,10 +15,12 @@ ...@@ -15,10 +15,12 @@
*/ */
#include <algorithm> #include <algorithm>
#include <atomic>
#include <folly/small_vector.h> #include <folly/small_vector.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <thread>
#include <type_traits> #include <type_traits>
#include <unistd.h> #include <unistd.h>
#include "folly/wangle/Executor.h" #include "folly/wangle/Executor.h"
...@@ -621,3 +623,37 @@ TEST(Future, throwIfFailed) { ...@@ -621,3 +623,37 @@ TEST(Future, throwIfFailed) {
EXPECT_NO_THROW(t.throwIfFailed()); EXPECT_NO_THROW(t.throwIfFailed());
}); });
} }
TEST(Future, waitWithSemaphoreImmediate) {
waitWithSemaphore(makeFuture());
auto done = waitWithSemaphore(makeFuture(42));
EXPECT_EQ(done, 42);
}
TEST(Future, waitWithSemaphore) {
Promise<int> p;
Future<int> f = p.getFuture();
std::atomic<bool> flag{false};
std::atomic<int> result{1};
std::atomic<std::thread::id> id;
std::thread t([&](Future<int>&& tf){
auto n = tf.then([&](Try<int> && t) {
id = std::this_thread::get_id();
return t.value();
});
flag = true;
result.store(waitWithSemaphore(std::move(n)));
LOG(INFO) << result;
},
std::move(f)
);
while(!flag){}
EXPECT_EQ(result.load(), 1);
p.setValue(42);
t.join();
// validate that the continuation ended up executing in this thread, which
// is more to ensure that this test actually tests what it should
EXPECT_EQ(id, std::this_thread::get_id());
EXPECT_EQ(result.load(), 42);
}
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