Commit a31665e0 authored by Pedro Eugenio Rocha Pedreira's avatar Pedro Eugenio Rocha Pedreira Committed by Facebook Github Bot

Propagate executor priority through FutureSplitter

Summary:
The futures created by FutureSplitter don't carry the orginal future's
executor priority, what makes it hard to guarantee that certain operations
based on a future chain run on a determined executor priority.

Reviewed By: WillerZ

Differential Revision: D9015012

fbshipit-source-id: 96b7e7ccb33583105f96d2170eeb159493b93ed0
parent 44cad810
......@@ -340,6 +340,11 @@ class FutureBase {
raise(FutureCancellation());
}
// Returns this future's executor priority.
int8_t getPriority() const {
return getCore().getPriority();
}
protected:
friend class Promise<T>;
template <class>
......@@ -834,6 +839,11 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// - `RESULT.valid() == true`
Future<T> toUnsafeFuture() &&;
/// Return this future's executor priority.
int8_t getPriority() const {
return Base::getPriority();
}
#if FOLLY_HAS_COROUTINES
class promise_type {
public:
......@@ -1812,6 +1822,11 @@ class Future : private futures::detail::FutureBase<T> {
return SemiFuture<T>{std::move(*this)};
}
// Returns this future's executor priority.
int8_t getPriority() const {
return Base::getPriority();
}
protected:
friend class Promise<T>;
template <class>
......
......@@ -32,8 +32,9 @@ class FOLLY_EXPORT FutureSplitterInvalid : public FutureException {
* FutureSplitter provides a `getFuture()' method which can be called multiple
* times, returning a new Future each time. These futures are completed when the
* original Future passed to the FutureSplitter constructor is completed, and
* are completed on the same executor (if any) as the original Future. Calls to
* `getFuture()' after that time return a completed Future.
* are completed on the same executor (if any) and at the same priority as the
* original Future. Calls to `getFuture()' after that time return a completed
* Future.
*/
template <class T>
class FutureSplitter {
......@@ -50,7 +51,8 @@ class FutureSplitter {
*/
explicit FutureSplitter(Future<T>&& future)
: promise_(std::make_shared<SharedPromise<T>>()),
e_(getExecutorFrom(future)) {
e_(getExecutorFrom(future)),
priority_(future.getPriority()) {
future.then([promise = promise_](Try<T>&& theTry) {
promise->setTry(std::move(theTry));
});
......@@ -63,7 +65,7 @@ class FutureSplitter {
if (promise_ == nullptr) {
throw_exception<FutureSplitterInvalid>();
}
return promise_->getSemiFuture().via(e_);
return promise_->getSemiFuture().via(e_, priority_);
}
/**
......@@ -79,6 +81,7 @@ class FutureSplitter {
private:
std::shared_ptr<SharedPromise<T>> promise_;
Executor* e_ = nullptr;
int8_t priority_{-1};
static Executor* getExecutorFrom(Future<T>& f) {
// If the passed future had a null executor, use an inline executor
......
......@@ -376,6 +376,10 @@ class Core final {
return executor_.get();
}
int8_t getPriority() const {
return priority_;
}
/// Call only from consumer thread
///
/// Eventual effect is to pass `e` to the Promise's interrupt handler, either
......
......@@ -174,3 +174,17 @@ TEST(FutureSplitter, splitFutureFailure) {
EXPECT_TRUE(f2.isReady());
EXPECT_TRUE(f2.hasException());
}
TEST(FutureSplitter, splitFuturePriority) {
std::vector<int8_t> priorities = {folly::Executor::LO_PRI,
folly::Executor::MID_PRI,
folly::Executor::HI_PRI};
for (const auto priority : priorities) {
Promise<int> p;
folly::FutureSplitter<int> sp(
p.getSemiFuture().via(&InlineExecutor::instance(), priority));
auto fut = sp.getFuture();
EXPECT_EQ(priority, fut.getPriority());
}
}
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