X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2Ffutures%2FFuture-inl.h;h=b353ff81a411f932c28da12af08239ff7ba48e32;hp=8df0d88cc1b5d287a72e0cd84ff77d58f968fb1e;hb=8e16a2eb93a5a2764e370b63ca0f210b140cc308;hpb=328b22e8a15d3fb725a2f46850ae9d18491d74af diff --git a/folly/futures/Future-inl.h b/folly/futures/Future-inl.h index 8df0d88c..b353ff81 100644 --- a/folly/futures/Future-inl.h +++ b/folly/futures/Future-inl.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ #define FOLLY_FUTURE_USING_FIBER 0 #else #define FOLLY_FUTURE_USING_FIBER 1 -#include +#include #endif namespace folly { @@ -62,29 +62,15 @@ Future& Future::operator=(Future&& other) noexcept { return *this; } -template -template -Future::Future(Future&& other) noexcept - : core_(detail::Core::convert(other.core_)) { - other.core_ = nullptr; -} - -template -template -Future& Future::operator=(Future&& other) noexcept { - std::swap(core_, detail::Core::convert(other.core_)); - return *this; -} - template template Future::Future(T2&& val) : core_(new detail::Core(Try(std::forward(val)))) {} template -template -Future::Future() - : core_(new detail::Core(Try(T()))) {} +template +Future::Future(typename std::enable_if::value>::type*) + : core_(new detail::Core(Try(T()))) {} template Future::~Future() { @@ -109,7 +95,7 @@ template template void Future::setCallback_(F&& func) { throwIfInvalid(); - core_->setCallback(std::move(func)); + core_->setCallback(std::forward(func)); } // unwrap @@ -131,19 +117,17 @@ Future::unwrap() { template template typename std::enable_if::type -Future::thenImplementation(F func, detail::argResult) { +Future::thenImplementation(F&& func, detail::argResult) { static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument"); typedef typename R::ReturnsFuture::Inner B; throwIfInvalid(); - // wrap these so we can move them into the lambda - folly::MoveWrapper> p; - p->core_->setInterruptHandlerNoLock(core_->getInterruptHandler()); - folly::MoveWrapper funcm(std::forward(func)); + Promise p; + p.core_->setInterruptHandlerNoLock(core_->getInterruptHandler()); // grab the Future now before we lose our handle on the Promise - auto f = p->getFuture(); + auto f = p.getFuture(); f.core_->setExecutorNoLock(getExecutor()); /* This is a bit tricky. @@ -165,9 +149,6 @@ Future::thenImplementation(F func, detail::argResult) { persist beyond the callback, if it gets moved), and so it is an optimization to just make it shared from the get-go. - We have to move in the Promise and func using the MoveWrapper - hack. (func could be copied but it's a big drag on perf). - Two subtle but important points about this design. detail::Core has no back pointers to Future or Promise, so if Future or Promise get moved (and they will be moved in performant code) we don't have to do @@ -179,15 +160,15 @@ Future::thenImplementation(F func, detail::argResult) { in the destruction of the Future used to create it. */ setCallback_( - [p, funcm](Try&& t) mutable { - if (!isTry && t.hasException()) { - p->setException(std::move(t.exception())); - } else { - p->setWith([&]() { - return (*funcm)(t.template get()...); - }); - } - }); + [ func = std::forward(func), pm = std::move(p) ](Try && t) mutable { + if (!isTry && t.hasException()) { + pm.setException(std::move(t.exception())); + } else { + pm.setWith([&]() { + return std::move(func)(t.template get()...); + }); + } + }); return f; } @@ -197,39 +178,43 @@ Future::thenImplementation(F func, detail::argResult) { template template typename std::enable_if::type -Future::thenImplementation(F func, detail::argResult) { +Future::thenImplementation(F&& func, detail::argResult) { static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument"); typedef typename R::ReturnsFuture::Inner B; throwIfInvalid(); - // wrap these so we can move them into the lambda - folly::MoveWrapper> p; - p->core_->setInterruptHandlerNoLock(core_->getInterruptHandler()); - folly::MoveWrapper funcm(std::forward(func)); + Promise p; + p.core_->setInterruptHandlerNoLock(core_->getInterruptHandler()); // grab the Future now before we lose our handle on the Promise - auto f = p->getFuture(); + auto f = p.getFuture(); f.core_->setExecutorNoLock(getExecutor()); - setCallback_( - [p, funcm](Try&& t) mutable { + setCallback_([ func = std::forward(func), pm = std::move(p) ]( + Try && t) mutable { + auto ew = [&] { if (!isTry && t.hasException()) { - p->setException(std::move(t.exception())); + return std::move(t.exception()); } else { try { - auto f2 = (*funcm)(t.template get()...); + auto f2 = std::move(func)(t.template get()...); // that didn't throw, now we can steal p - f2.setCallback_([p](Try&& b) mutable { - p->setTry(std::move(b)); + f2.setCallback_([p = std::move(pm)](Try && b) mutable { + p.setTry(std::move(b)); }); + return exception_wrapper(); } catch (const std::exception& e) { - p->setException(exception_wrapper(std::current_exception(), e)); + return exception_wrapper(std::current_exception(), e); } catch (...) { - p->setException(exception_wrapper(std::current_exception())); + return exception_wrapper(std::current_exception()); } } - }); + }(); + if (ew) { + pm.setException(std::move(ew)); + } + }); return f; } @@ -279,17 +264,15 @@ Future::onError(F&& func) { Promise p; p.core_->setInterruptHandlerNoLock(core_->getInterruptHandler()); auto f = p.getFuture(); - auto pm = folly::makeMoveWrapper(std::move(p)); - auto funcm = folly::makeMoveWrapper(std::move(func)); - setCallback_([pm, funcm](Try&& t) mutable { - if (!t.template withException([&] (Exn& e) { - pm->setWith([&]{ - return (*funcm)(e); - }); - })) { - pm->setTry(std::move(t)); - } - }); + + setCallback_( + [ func = std::forward(func), pm = std::move(p) ](Try && t) mutable { + if (!t.template withException([&](Exn& e) { + pm.setWith([&] { return std::move(func)(e); }); + })) { + pm.setTry(std::move(t)); + } + }); return f; } @@ -309,22 +292,28 @@ Future::onError(F&& func) { Promise p; auto f = p.getFuture(); - auto pm = folly::makeMoveWrapper(std::move(p)); - auto funcm = folly::makeMoveWrapper(std::move(func)); - setCallback_([pm, funcm](Try&& t) mutable { - if (!t.template withException([&] (Exn& e) { - try { - auto f2 = (*funcm)(e); - f2.setCallback_([pm](Try&& t2) mutable { - pm->setTry(std::move(t2)); - }); - } catch (const std::exception& e2) { - pm->setException(exception_wrapper(std::current_exception(), e2)); - } catch (...) { - pm->setException(exception_wrapper(std::current_exception())); + + setCallback_([ pm = std::move(p), func = std::forward(func) ]( + Try && t) mutable { + if (!t.template withException([&](Exn& e) { + auto ew = [&] { + try { + auto f2 = std::move(func)(e); + f2.setCallback_([pm = std::move(pm)](Try && t2) mutable { + pm.setTry(std::move(t2)); + }); + return exception_wrapper(); + } catch (const std::exception& e2) { + return exception_wrapper(std::current_exception(), e2); + } catch (...) { + return exception_wrapper(std::current_exception()); + } + }(); + if (ew) { + pm.setException(std::move(ew)); } })) { - pm->setTry(std::move(t)); + pm.setTry(std::move(t)); } }); @@ -333,10 +322,9 @@ Future::onError(F&& func) { template template -Future Future::ensure(F func) { - MoveWrapper funcw(std::move(func)); - return this->then([funcw](Try&& t) mutable { - (*funcw)(); +Future Future::ensure(F&& func) { + return this->then([funcw = std::forward(func)](Try && t) mutable { + funcw(); return makeFuture(std::move(t)); }); } @@ -344,17 +332,15 @@ Future Future::ensure(F func) { template template Future Future::onTimeout(Duration dur, F&& func, Timekeeper* tk) { - auto funcw = folly::makeMoveWrapper(std::forward(func)); - return within(dur, tk) - .onError([funcw](TimedOut const&) { return (*funcw)(); }); + return within(dur, tk).onError([funcw = std::forward(func)]( + TimedOut const&) { return funcw(); }); } template template -typename std::enable_if< - detail::callableWith::value && - detail::Extract::ReturnsFuture::value, - Future>::type +typename std::enable_if::value && + detail::Extract::ReturnsFuture::value, + Future>::type Future::onError(F&& func) { static_assert( std::is_same::Return, Future>::value, @@ -362,24 +348,29 @@ Future::onError(F&& func) { Promise p; auto f = p.getFuture(); - auto pm = folly::makeMoveWrapper(std::move(p)); - auto funcm = folly::makeMoveWrapper(std::move(func)); - setCallback_([pm, funcm](Try t) mutable { - if (t.hasException()) { - try { - auto f2 = (*funcm)(std::move(t.exception())); - f2.setCallback_([pm](Try t2) mutable { - pm->setTry(std::move(t2)); - }); - } catch (const std::exception& e2) { - pm->setException(exception_wrapper(std::current_exception(), e2)); - } catch (...) { - pm->setException(exception_wrapper(std::current_exception())); - } - } else { - pm->setTry(std::move(t)); - } - }); + setCallback_( + [ pm = std::move(p), func = std::forward(func) ](Try t) mutable { + if (t.hasException()) { + auto ew = [&] { + try { + auto f2 = std::move(func)(std::move(t.exception())); + f2.setCallback_([pm = std::move(pm)](Try t2) mutable { + pm.setTry(std::move(t2)); + }); + return exception_wrapper(); + } catch (const std::exception& e2) { + return exception_wrapper(std::current_exception(), e2); + } catch (...) { + return exception_wrapper(std::current_exception()); + } + }(); + if (ew) { + pm.setException(std::move(ew)); + } + } else { + pm.setTry(std::move(t)); + } + }); return f; } @@ -398,17 +389,14 @@ Future::onError(F&& func) { Promise p; auto f = p.getFuture(); - auto pm = folly::makeMoveWrapper(std::move(p)); - auto funcm = folly::makeMoveWrapper(std::move(func)); - setCallback_([pm, funcm](Try t) mutable { - if (t.hasException()) { - pm->setWith([&]{ - return (*funcm)(std::move(t.exception())); + setCallback_( + [ pm = std::move(p), func = std::forward(func) ](Try t) mutable { + if (t.hasException()) { + pm.setWith([&] { return std::move(func)(std::move(t.exception())); }); + } else { + pm.setTry(std::move(t)); + } }); - } else { - pm->setTry(std::move(t)); - } - }); return f; } @@ -434,6 +422,11 @@ Try& Future::getTry() { return core_->getTry(); } +template +Try& Future::getTryVia(DrivableExecutor* e) { + return waitVia(e).getTry(); +} + template Optional> Future::poll() { Optional> o; @@ -456,19 +449,18 @@ template inline Future Future::via(Executor* executor, int8_t priority) & { throwIfInvalid(); - MoveWrapper> p; - auto f = p->getFuture(); - then([p](Try&& t) mutable { p->setTry(std::move(t)); }); + Promise p; + auto f = p.getFuture(); + then([p = std::move(p)](Try && t) mutable { p.setTry(std::move(t)); }); return std::move(f).via(executor, priority); } - template -auto via(Executor* x, Func func) +auto via(Executor* x, Func&& func) -> Future::Inner> { // TODO make this actually more performant. :-P #7260175 - return via(x).then(func); + return via(x).then(std::forward(func)); } template @@ -597,7 +589,7 @@ collectAll(InputIterator first, InputIterator last) { typename std::iterator_traits::value_type::value_type T; struct CollectAllContext { - CollectAllContext(int n) : results(n) {} + CollectAllContext(size_t n) : results(n) {} ~CollectAllContext() { p.setValue(std::move(results)); } @@ -605,7 +597,8 @@ collectAll(InputIterator first, InputIterator last) { std::vector> results; }; - auto ctx = std::make_shared(std::distance(first, last)); + auto ctx = + std::make_shared(size_t(std::distance(first, last))); mapSetCallback(first, last, [ctx](size_t i, Try&& t) { ctx->results[i] = std::move(t); }); @@ -632,7 +625,7 @@ struct CollectContext { Nothing, std::vector>>::type; - explicit CollectContext(int n) : result(n) {} + explicit CollectContext(size_t n) : result(n) {} ~CollectContext() { if (!threw.exchange(true)) { // map Optional -> T @@ -701,7 +694,7 @@ collectAny(InputIterator first, InputIterator last) { typename std::iterator_traits::value_type::value_type T; struct CollectAnyContext { - CollectAnyContext() {}; + CollectAnyContext() {} Promise>> p; std::atomic done {false}; }; @@ -715,6 +708,37 @@ collectAny(InputIterator first, InputIterator last) { return ctx->p.getFuture(); } +// collectAnyWithoutException (iterator) + +template +Future::value_type::value_type>> +collectAnyWithoutException(InputIterator first, InputIterator last) { + typedef + typename std::iterator_traits::value_type::value_type T; + + struct CollectAnyWithoutExceptionContext { + CollectAnyWithoutExceptionContext(){} + Promise> p; + std::atomic done{false}; + std::atomic nFulfilled{0}; + size_t nTotal; + }; + + auto ctx = std::make_shared(); + ctx->nTotal = size_t(std::distance(first, last)); + + mapSetCallback(first, last, [ctx](size_t i, Try&& t) { + if (!t.hasException() && !ctx->done.exchange(true)) { + ctx->p.setValue(std::make_pair(i, std::move(t.value()))); + } else if (++ctx->nFulfilled == ctx->nTotal) { + ctx->p.setException(t.exception()); + } + }); + return ctx->p.getFuture(); +} + // collectN (iterator) template @@ -762,17 +786,19 @@ Future reduce(It first, It last, T&& initial, F&& func) { } typedef typename std::iterator_traits::value_type::value_type ItT; - typedef typename std::conditional< - detail::callableWith&&>::value, Try, ItT>::type Arg; + typedef + typename std::conditional&&>::value, + Try, + ItT>::type Arg; typedef isTry IsTry; - folly::MoveWrapper minitial(std::move(initial)); auto sfunc = std::make_shared(std::move(func)); - auto f = first->then([minitial, sfunc](Try& head) mutable { - return (*sfunc)(std::move(*minitial), - head.template get()); - }); + auto f = first->then( + [ minitial = std::move(initial), sfunc ](Try & head) mutable { + return (*sfunc)( + std::move(minitial), head.template get()); + }); for (++first; first != last; ++first) { f = collectAll(f, *first).then([sfunc](std::tuple, Try>& t) { @@ -840,12 +866,13 @@ window(Collection input, F func, size_t n) { template template Future Future::reduce(I&& initial, F&& func) { - folly::MoveWrapper minitial(std::move(initial)); - folly::MoveWrapper mfunc(std::move(func)); - return then([minitial, mfunc](T& vals) mutable { - auto ret = std::move(*minitial); + return then([ + minitial = std::forward(initial), + mfunc = std::forward(func) + ](T& vals) mutable { + auto ret = std::move(minitial); for (auto& val : vals) { - ret = (*mfunc)(std::move(ret), std::move(val)); + ret = mfunc(std::move(ret), std::move(val)); } return ret; }); @@ -865,7 +892,7 @@ Future unorderedReduce(It first, It last, T initial, F func) { UnorderedReduceContext(T&& memo, F&& fn, size_t n) : lock_(), memo_(makeFuture(std::move(memo))), func_(std::move(fn)), numThens_(0), numFutures_(n), promise_() - {}; + {} folly::MicroSpinLock lock_; // protects memo_ and numThens_ Future memo_; F func_; @@ -881,19 +908,19 @@ Future unorderedReduce(It first, It last, T initial, F func) { first, last, [ctx](size_t /* i */, Try&& t) { - folly::MoveWrapper> mt(std::move(t)); // Futures can be completed in any order, simultaneously. // To make this non-blocking, we create a new Future chain in // the order of completion to reduce the values. // The spinlock just protects chaining a new Future, not actually // executing the reduce, which should be really fast. folly::MSLGuard lock(ctx->lock_); - ctx->memo_ = ctx->memo_.then([ctx, mt](T&& v) mutable { - // Either return a ItT&& or a Try&& depending - // on the type of the argument of func. - return ctx->func_(std::move(v), - mt->template get()); - }); + ctx->memo_ = + ctx->memo_.then([ ctx, mt = std::move(t) ](T && v) mutable { + // Either return a ItT&& or a Try&& depending + // on the type of the argument of func. + return ctx->func_(std::move(v), + mt.template get()); + }); if (++ctx->numThens_ == ctx->numFutures_) { // After reducing the value of the last Future, fulfill the Promise ctx->memo_.setCallback_( @@ -980,13 +1007,15 @@ void waitImpl(Future& f) { template void waitImpl(Future& f, Duration dur) { // short-circuit if there's nothing to do - if (f.isReady()) return; + if (f.isReady()) { + return; + } - folly::MoveWrapper> promise; - auto ret = promise->getFuture(); + Promise promise; + auto ret = promise.getFuture(); auto baton = std::make_shared(); - f.setCallback_([baton, promise](Try&& t) mutable { - promise->setTry(std::move(t)); + f.setCallback_([ baton, promise = std::move(promise) ](Try && t) mutable { + promise.setTry(std::move(t)); baton->post(); }); f = std::move(ret); @@ -1002,7 +1031,7 @@ void waitViaImpl(Future& f, DrivableExecutor* e) { // always have a callback to satisfy it if (f.isReady()) return; - f = f.then([](T&& t) { return std::move(t); }); + f = f.via(e).then([](T&& t) { return std::move(t); }); while (!f.isReady()) { e->drive(); } @@ -1089,11 +1118,10 @@ Future Future::willEqual(Future& f) { template template -Future Future::filter(F predicate) { - auto p = folly::makeMoveWrapper(std::move(predicate)); - return this->then([p](T val) { +Future Future::filter(F&& predicate) { + return this->then([p = std::forward(predicate)](T val) { T const& valConstRef = val; - if (!(*p)(valConstRef)) { + if (!p(valConstRef)) { throw PredicateDoesNotObtain(); } return val; @@ -1141,28 +1169,31 @@ auto Future::thenMultiWithExecutor(Executor* x, Callback&& fn) } template -inline Future when(bool p, F thunk) { - return p ? thunk().unit() : makeFuture(); +inline Future when(bool p, F&& thunk) { + return p ? std::forward(thunk)().unit() : makeFuture(); } template -Future whileDo(P predicate, F thunk) { +Future whileDo(P&& predicate, F&& thunk) { if (predicate()) { - return thunk().then([=] { - return whileDo(predicate, thunk); + auto future = thunk(); + return future.then([ + predicate = std::forward

(predicate), + thunk = std::forward(thunk) + ]() mutable { + return whileDo(std::forward

(predicate), std::forward(thunk)); }); } return makeFuture(); } template -Future times(const int n, F thunk) { - auto count = folly::makeMoveWrapper( - std::unique_ptr>(new std::atomic(0)) - ); - return folly::whileDo([=]() mutable { - return (*count)->fetch_add(1) < n; - }, thunk); +Future times(const int n, F&& thunk) { + return folly::whileDo( + [ n, count = folly::make_unique>(0) ]() mutable { + return count->fetch_add(1) < n; + }, + std::forward(thunk)); } namespace futures { @@ -1198,31 +1229,56 @@ struct retrying_policy_traits { is_fut::value, retrying_policy_fut_tag, void>::type>::type; }; +template +void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) { + using F = typename std::result_of::type; + using T = typename F::value_type; + auto f = ff(k++); + f.then([ + k, + prom = std::move(prom), + pm = std::forward(p), + ffm = std::forward(ff) + ](Try && t) mutable { + if (t.hasValue()) { + prom.setValue(std::move(t).value()); + return; + } + auto& x = t.exception(); + auto q = pm(k, x); + q.then([ + k, + prom = std::move(prom), + xm = std::move(x), + pm = std::move(pm), + ffm = std::move(ffm) + ](bool shouldRetry) mutable { + if (shouldRetry) { + retryingImpl(k, std::move(pm), std::move(ffm), std::move(prom)); + } else { + prom.setException(std::move(xm)); + }; + }); + }); +} + template typename std::result_of::type retrying(size_t k, Policy&& p, FF&& ff) { using F = typename std::result_of::type; using T = typename F::value_type; - auto f = ff(k++); - auto pm = makeMoveWrapper(p); - auto ffm = makeMoveWrapper(ff); - return f.onError([=](exception_wrapper x) mutable { - auto q = (*pm)(k, x); - auto xm = makeMoveWrapper(std::move(x)); - return q.then([=](bool r) mutable { - return r - ? retrying(k, pm.move(), ffm.move()) - : makeFuture(xm.move()); - }); - }); + auto prom = Promise(); + auto f = prom.getFuture(); + retryingImpl( + k, std::forward(p), std::forward(ff), std::move(prom)); + return f; } template typename std::result_of::type retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) { - auto pm = makeMoveWrapper(std::move(p)); - auto q = [=](size_t k, exception_wrapper x) { - return makeFuture((*pm)(k, x)); + auto q = [pm = std::forward(p)](size_t k, exception_wrapper x) { + return makeFuture(pm(k, x)); }; return retrying(0, std::move(q), std::forward(ff)); } @@ -1255,18 +1311,29 @@ retryingPolicyCappedJitteredExponentialBackoff( Duration backoff_min, Duration backoff_max, double jitter_param, - URNG rng, + URNG&& rng, Policy&& p) { - auto pm = makeMoveWrapper(std::move(p)); - auto rngp = std::make_shared(std::move(rng)); - return [=](size_t n, const exception_wrapper& ex) mutable { - if (n == max_tries) { return makeFuture(false); } - return (*pm)(n, ex).then([=](bool v) { - if (!v) { return makeFuture(false); } - auto backoff = detail::retryingJitteredExponentialBackoffDur( - n, backoff_min, backoff_max, jitter_param, *rngp); - return futures::sleep(backoff).then([] { return true; }); - }); + return [ + pm = std::forward(p), + max_tries, + backoff_min, + backoff_max, + jitter_param, + rngp = std::forward(rng) + ](size_t n, const exception_wrapper& ex) mutable { + if (n == max_tries) { + return makeFuture(false); + } + return pm(n, ex).then( + [ n, backoff_min, backoff_max, jitter_param, rngp = std::move(rngp) ]( + bool v) mutable { + if (!v) { + return makeFuture(false); + } + auto backoff = detail::retryingJitteredExponentialBackoffDur( + n, backoff_min, backoff_max, jitter_param, rngp); + return futures::sleep(backoff).then([] { return true; }); + }); }; } @@ -1277,19 +1344,19 @@ retryingPolicyCappedJitteredExponentialBackoff( Duration backoff_min, Duration backoff_max, double jitter_param, - URNG rng, + URNG&& rng, Policy&& p, retrying_policy_raw_tag) { - auto pm = makeMoveWrapper(std::move(p)); - auto q = [=](size_t n, const exception_wrapper& e) { - return makeFuture((*pm)(n, e)); + auto q = [pm = std::forward(p)]( + size_t n, const exception_wrapper& e) { + return makeFuture(pm(n, e)); }; return retryingPolicyCappedJitteredExponentialBackoff( max_tries, backoff_min, backoff_max, jitter_param, - std::move(rng), + std::forward(rng), std::move(q)); } @@ -1300,7 +1367,7 @@ retryingPolicyCappedJitteredExponentialBackoff( Duration backoff_min, Duration backoff_max, double jitter_param, - URNG rng, + URNG&& rng, Policy&& p, retrying_policy_fut_tag) { return retryingPolicyCappedJitteredExponentialBackoff( @@ -1308,10 +1375,9 @@ retryingPolicyCappedJitteredExponentialBackoff( backoff_min, backoff_max, jitter_param, - std::move(rng), - std::move(p)); + std::forward(rng), + std::forward(p)); } - } template @@ -1335,7 +1401,7 @@ retryingPolicyCappedJitteredExponentialBackoff( Duration backoff_min, Duration backoff_max, double jitter_param, - URNG rng, + URNG&& rng, Policy&& p) { using tag = typename detail::retrying_policy_traits::tag; return detail::retryingPolicyCappedJitteredExponentialBackoff( @@ -1343,8 +1409,8 @@ retryingPolicyCappedJitteredExponentialBackoff( backoff_min, backoff_max, jitter_param, - std::move(rng), - std::move(p), + std::forward(rng), + std::forward(p), tag()); }