X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Fwangle%2FFuture-inl.h;h=cf10e9f466a280c5b3d68a8992a4c97878ff75ec;hb=ff25b9504aa16da11906eb2807fd37127cb7c538;hp=a8f5b94bb1da2d162981b43d9fbacd521f00d70f;hpb=73e3273b9f2f2f5686e2b6ff4a87d572f2391585;p=folly.git diff --git a/folly/wangle/Future-inl.h b/folly/wangle/Future-inl.h index a8f5b94b..cf10e9f4 100644 --- a/folly/wangle/Future-inl.h +++ b/folly/wangle/Future-inl.h @@ -16,8 +16,11 @@ #pragma once -#include "detail/State.h" -#include +#include +#include + +#include +#include namespace folly { namespace wangle { @@ -32,13 +35,13 @@ struct isFuture > { }; template -Future::Future(Future&& other) noexcept : state_(nullptr) { +Future::Future(Future&& other) noexcept : core_(nullptr) { *this = std::move(other); } template Future& Future::operator=(Future&& other) { - std::swap(state_, other.state_); + std::swap(core_, other.core_); return *this; } @@ -49,15 +52,15 @@ Future::~Future() { template void Future::detach() { - if (state_) { - state_->detachFuture(); - state_ = nullptr; + if (core_) { + core_->detachFuture(); + core_ = nullptr; } } template void Future::throwIfInvalid() const { - if (!state_) + if (!core_) throw NoState(); } @@ -65,7 +68,7 @@ template template void Future::setCallback_(F&& func) { throwIfInvalid(); - state_->setCallback(std::move(func)); + core_->setCallback(std::move(func)); } template @@ -92,10 +95,10 @@ Future::then(F&& func) { sophisticated that avoids making a new Future object when it can, as an optimization. But this is correct. - state_ can't be moved, it is explicitly disallowed (as is copying). But + core_ can't be moved, it is explicitly disallowed (as is copying). But if there's ever a reason to allow it, this is one place that makes that assumption and would need to be fixed. We use a standard shared pointer - for state_ (by copying it in), which means in essence obj holds a shared + for core_ (by copying it in), which means in essence obj holds a shared pointer to itself. But this shouldn't leak because Promise will not outlive the continuation, because Promise will setException() with a broken Promise if it is destructed before completed. We could use a @@ -107,11 +110,11 @@ Future::then(F&& func) { 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::State has no + 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 anything fancy. And because we store the continuation in the - detail::State, not in the Future, we can execute the continuation even + detail::Core, not in the Future, we can execute the continuation even after the Future has gone out of scope. This is an intentional design decision. It is likely we will want to be able to cancel a continuation in some circumstances, but I think it should be explicit not implicit @@ -169,42 +172,38 @@ template typename std::add_lvalue_reference::type Future::value() { throwIfInvalid(); - return state_->value(); + return core_->value(); } template typename std::add_lvalue_reference::type Future::value() const { throwIfInvalid(); - return state_->value(); + return core_->value(); } template Try& Future::getTry() { throwIfInvalid(); - return state_->getTry(); + return core_->getTry(); } template template inline Future Future::via(Executor* executor) { throwIfInvalid(); - auto f = then([=](Try&& t) { - MoveWrapper> promise; - MoveWrapper> tw(std::move(t)); - auto f = promise->getFuture(); - executor->add([=]() mutable { promise->fulfilTry(std::move(*tw)); }); - return f; - }); - f.deactivate(); - return f; + + this->deactivate(); + core_->setExecutor(executor); + + return std::move(*this); } template bool Future::isReady() const { throwIfInvalid(); - return state_->ready(); + return core_->ready(); } // makeFuture @@ -254,7 +253,8 @@ Future makeFuture(std::exception_ptr const& e) { } template -typename std::enable_if::value, Future>::type +typename std::enable_if::value, + Future>::type makeFuture(E const& e) { Promise p; auto f = p.getFuture(); @@ -408,61 +408,59 @@ whenN(InputIterator first, InputIterator last, size_t n) { template Future waitWithSemaphore(Future&& f) { - LifoSem sem; + Baton<> baton; auto done = f.then([&](Try &&t) { - sem.post(); + baton.post(); return std::move(t.value()); }); - sem.wait(); + 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 waitWithSemaphore(Future&& f) { - LifoSem sem; + Baton<> baton; auto done = f.then([&](Try &&t) { - sem.post(); + baton.post(); t.value(); }); - sem.wait(); + 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 Future waitWithSemaphore(Future&& f, Duration timeout) { - auto sem = std::make_shared(); - auto done = f.then([sem](Try &&t) { - sem->post(); + auto baton = std::make_shared>(); + auto done = f.then([baton](Try &&t) { + baton->post(); return std::move(t.value()); }); - std::thread t([sem, timeout](){ - std::this_thread::sleep_for(timeout); - sem->shutdown(); - }); - t.detach(); - try { - sem->wait(); - } catch (ShutdownSemError & ign) { } + baton->timed_wait(std::chrono::system_clock::now() + timeout); return done; } template Future waitWithSemaphore(Future&& f, Duration timeout) { - auto sem = std::make_shared(); - auto done = f.then([sem](Try &&t) { - sem->post(); + auto baton = std::make_shared>(); + auto done = f.then([baton](Try &&t) { + baton->post(); t.value(); }); - std::thread t([sem, timeout](){ - std::this_thread::sleep_for(timeout); - sem->shutdown(); - }); - t.detach(); - try { - sem->wait(); - } catch (ShutdownSemError & ign) { } + baton->timed_wait(std::chrono::system_clock::now() + timeout); return done; }