#pragma once
-#include "detail.h"
+#include "detail/State.h"
#include <folly/LifoSem.h>
namespace folly { namespace wangle {
};
template <class T>
-Future<T>::Future(Future<T>&& other) noexcept : obj_(other.obj_) {
- other.obj_ = nullptr;
+Future<T>::Future(Future<T>&& other) noexcept : state_(other.state_) {
+ other.state_ = nullptr;
}
template <class T>
Future<T>& Future<T>::operator=(Future<T>&& other) {
- std::swap(obj_, other.obj_);
+ std::swap(state_, other.state_);
return *this;
}
template <class T>
Future<T>::~Future() {
- if (obj_) {
+ if (state_) {
setCallback_([](Try<T>&&) {}); // detach
}
}
template <class T>
void Future<T>::throwIfInvalid() const {
- if (!obj_)
+ if (!state_)
throw NoState();
}
template <class F>
void Future<T>::setCallback_(F&& func) {
throwIfInvalid();
- obj_->setCallback_(std::move(func));
- obj_ = nullptr;
+ state_->setCallback_(std::move(func));
+ state_ = nullptr;
}
template <class T>
sophisticated that avoids making a new Future object when it can, as an
optimization. But this is correct.
- obj_ can't be moved, it is explicitly disallowed (as is copying). But
+ state_ 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 obj_ (by copying it in), which means in essence obj holds a shared
+ for state_ (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
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. FutureObject has no
+ Two subtle but important points about this design. detail::State 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
- FutureObject, not in the Future, we can execute the continuation even
+ detail::State, 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
typename std::add_lvalue_reference<T>::type Future<T>::value() {
throwIfInvalid();
- return obj_->value();
+ return state_->value();
}
template <class T>
typename std::add_lvalue_reference<const T>::type Future<T>::value() const {
throwIfInvalid();
- return obj_->value();
+ return state_->value();
}
template <class T>
Try<T>& Future<T>::getTry() {
throwIfInvalid();
- return obj_->getTry();
+ return state_->getTry();
}
template <class T>
template <class T>
bool Future<T>::isReady() const {
throwIfInvalid();
- return obj_->ready();
+ return state_->ready();
}
// makeFuture
void setCallback_(F&& func);
private:
- typedef detail::FutureObject<T>* objPtr;
+ typedef detail::State<T>* statePtr;
// shared state object
- objPtr obj_;
+ statePtr state_;
explicit
- Future(objPtr obj) : obj_(obj) {}
+ Future(statePtr obj) : state_(obj) {}
void throwIfInvalid() const;
#include <thread>
#include "WangleException.h"
-#include "detail.h"
+#include "detail/State.h"
namespace folly { namespace wangle {
template <class T>
-Promise<T>::Promise() : retrieved_(false), obj_(new detail::FutureObject<T>())
+Promise<T>::Promise() : retrieved_(false), state_(new detail::State<T>())
{}
template <class T>
Promise<T>::Promise(Promise<T>&& other) :
-retrieved_(other.retrieved_), obj_(other.obj_) {
- other.obj_ = nullptr;
+retrieved_(other.retrieved_), state_(other.state_) {
+ other.state_ = nullptr;
}
template <class T>
Promise<T>& Promise<T>::operator=(Promise<T>&& other) {
- std::swap(obj_, other.obj_);
+ std::swap(state_, other.state_);
std::swap(retrieved_, other.retrieved_);
return *this;
}
template <class T>
void Promise<T>::throwIfFulfilled() {
- if (!obj_)
+ if (!state_)
throw PromiseAlreadySatisfied();
}
template <class T>
Promise<T>::~Promise() {
- if (obj_) {
+ if (state_) {
setException(BrokenPromise());
}
}
throwIfRetrieved();
throwIfFulfilled();
retrieved_ = true;
- return Future<T>(obj_);
+ return Future<T>(state_);
}
template <class T>
template <class T>
void Promise<T>::setException(std::exception_ptr const& e) {
throwIfFulfilled();
- obj_->setException(e);
+ state_->setException(e);
if (!retrieved_) {
- delete obj_;
+ delete state_;
}
- obj_ = nullptr;
+ state_ = nullptr;
}
template <class T>
void Promise<T>::fulfilTry(Try<T>&& t) {
throwIfFulfilled();
- obj_->fulfil(std::move(t));
+ state_->fulfil(std::move(t));
if (!retrieved_) {
- delete obj_;
+ delete state_;
}
- obj_ = nullptr;
+ state_ = nullptr;
}
template <class T>
"Use setValue() instead");
throwIfFulfilled();
- obj_->fulfil(Try<T>(std::forward<M>(v)));
+ state_->fulfil(Try<T>(std::forward<M>(v)));
if (!retrieved_) {
- delete obj_;
+ delete state_;
}
- obj_ = nullptr;
+ state_ = nullptr;
}
template <class T>
"Use setValue(value) instead");
throwIfFulfilled();
- obj_->fulfil(Try<void>());
+ state_->fulfil(Try<void>());
if (!retrieved_) {
- delete obj_;
+ delete state_;
}
- obj_ = nullptr;
+ state_ = nullptr;
}
template <class T>
void fulfil(F&& func);
private:
- typedef typename Future<T>::objPtr objPtr;
+ typedef typename Future<T>::statePtr statePtr;
// Whether the Future has been retrieved (a one-time operation).
bool retrieved_;
// shared state object
- objPtr obj_;
+ statePtr state_;
void throwIfFulfilled();
void throwIfRetrieved();
+++ /dev/null
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <atomic>
-#include <folly/Optional.h>
-#include <stdexcept>
-#include <vector>
-
-#include "Try.h"
-#include "Promise.h"
-#include "Future.h"
-
-namespace folly { namespace wangle { namespace detail {
-
-/** The shared state object for Future and Promise. */
-template<typename T>
-class FutureObject {
- public:
- FutureObject() = default;
-
- // not copyable
- FutureObject(FutureObject const&) = delete;
- FutureObject& operator=(FutureObject const&) = delete;
-
- // not movable (see comment in the implementation of Future::then)
- FutureObject(FutureObject&&) noexcept = delete;
- FutureObject& operator=(FutureObject&&) = delete;
-
- Try<T>& getTry() {
- return *value_;
- }
-
- template <typename F>
- void setCallback_(F func) {
- if (continuation_) {
- throw std::logic_error("setCallback_ called twice");
- }
-
- continuation_ = std::move(func);
-
- if (shouldContinue_.test_and_set()) {
- continuation_(std::move(*value_));
- delete this;
- }
- }
-
- void fulfil(Try<T>&& t) {
- if (value_.hasValue()) {
- throw std::logic_error("fulfil called twice");
- }
-
- value_ = std::move(t);
-
- if (shouldContinue_.test_and_set()) {
- continuation_(std::move(*value_));
- delete this;
- }
- }
-
- void setException(std::exception_ptr const& e) {
- fulfil(Try<T>(e));
- }
-
- template <class E> void setException(E const& e) {
- fulfil(Try<T>(std::make_exception_ptr<E>(e)));
- }
-
- bool ready() const {
- return value_.hasValue();
- }
-
- typename std::add_lvalue_reference<T>::type value() {
- if (ready()) {
- return value_->value();
- } else {
- throw FutureNotReady();
- }
- }
-
- private:
- std::atomic_flag shouldContinue_ = ATOMIC_FLAG_INIT;
- folly::Optional<Try<T>> value_;
- std::function<void(Try<T>&&)> continuation_;
-};
-
-template <typename... Ts>
-struct VariadicContext {
- VariadicContext() : total(0), count(0) {}
- Promise<std::tuple<Try<Ts>... > > p;
- std::tuple<Try<Ts>... > results;
- size_t total;
- std::atomic<size_t> count;
- typedef Future<std::tuple<Try<Ts>...>> type;
-};
-
-template <typename... Ts, typename THead, typename... Fs>
-typename std::enable_if<sizeof...(Fs) == 0, void>::type
-whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
- head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
- std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
- if (++ctx->count == ctx->total) {
- ctx->p.setValue(std::move(ctx->results));
- delete ctx;
- }
- });
-}
-
-template <typename... Ts, typename THead, typename... Fs>
-typename std::enable_if<sizeof...(Fs) != 0, void>::type
-whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
- head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
- std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
- if (++ctx->count == ctx->total) {
- ctx->p.setValue(std::move(ctx->results));
- delete ctx;
- }
- });
- // template tail-recursion
- whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
-}
-
-template <typename T>
-struct WhenAllContext {
- explicit WhenAllContext() : count(0), total(0) {}
- Promise<std::vector<Try<T> > > p;
- std::vector<Try<T> > results;
- std::atomic<size_t> count;
- size_t total;
-};
-
-template <typename T>
-struct WhenAnyContext {
- explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
- Promise<std::pair<size_t, Try<T>>> p;
- std::atomic<bool> done;
- std::atomic<size_t> ref_count;
- void decref() {
- if (--ref_count == 0) {
- delete this;
- }
- }
-};
-
-}}} // namespace
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <stdexcept>
+#include <vector>
+
+#include <folly/Optional.h>
+
+#include <folly/wangle/Try.h>
+#include <folly/wangle/Promise.h>
+#include <folly/wangle/Future.h>
+
+namespace folly { namespace wangle { namespace detail {
+
+/** The shared state object for Future and Promise. */
+template<typename T>
+class State {
+ public:
+ State() = default;
+
+ // not copyable
+ State(State const&) = delete;
+ State& operator=(State const&) = delete;
+
+ // not movable (see comment in the implementation of Future::then)
+ State(State&&) noexcept = delete;
+ State& operator=(State&&) = delete;
+
+ Try<T>& getTry() {
+ return *value_;
+ }
+
+ template <typename F>
+ void setCallback_(F func) {
+ if (continuation_) {
+ throw std::logic_error("setCallback_ called twice");
+ }
+
+ continuation_ = std::move(func);
+
+ if (shouldContinue_.test_and_set()) {
+ continuation_(std::move(*value_));
+ delete this;
+ }
+ }
+
+ void fulfil(Try<T>&& t) {
+ if (value_.hasValue()) {
+ throw std::logic_error("fulfil called twice");
+ }
+
+ value_ = std::move(t);
+
+ if (shouldContinue_.test_and_set()) {
+ continuation_(std::move(*value_));
+ delete this;
+ }
+ }
+
+ void setException(std::exception_ptr const& e) {
+ fulfil(Try<T>(e));
+ }
+
+ template <class E> void setException(E const& e) {
+ fulfil(Try<T>(std::make_exception_ptr<E>(e)));
+ }
+
+ bool ready() const {
+ return value_.hasValue();
+ }
+
+ typename std::add_lvalue_reference<T>::type value() {
+ if (ready()) {
+ return value_->value();
+ } else {
+ throw FutureNotReady();
+ }
+ }
+
+ private:
+ std::atomic_flag shouldContinue_ = ATOMIC_FLAG_INIT;
+ folly::Optional<Try<T>> value_;
+ std::function<void(Try<T>&&)> continuation_;
+};
+
+template <typename... Ts>
+struct VariadicContext {
+ VariadicContext() : total(0), count(0) {}
+ Promise<std::tuple<Try<Ts>... > > p;
+ std::tuple<Try<Ts>... > results;
+ size_t total;
+ std::atomic<size_t> count;
+ typedef Future<std::tuple<Try<Ts>...>> type;
+};
+
+template <typename... Ts, typename THead, typename... Fs>
+typename std::enable_if<sizeof...(Fs) == 0, void>::type
+whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
+ head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
+ std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
+ if (++ctx->count == ctx->total) {
+ ctx->p.setValue(std::move(ctx->results));
+ delete ctx;
+ }
+ });
+}
+
+template <typename... Ts, typename THead, typename... Fs>
+typename std::enable_if<sizeof...(Fs) != 0, void>::type
+whenAllVariadicHelper(VariadicContext<Ts...> *ctx, THead&& head, Fs&&... tail) {
+ head.setCallback_([ctx](Try<typename THead::value_type>&& t) {
+ std::get<sizeof...(Ts) - sizeof...(Fs) - 1>(ctx->results) = std::move(t);
+ if (++ctx->count == ctx->total) {
+ ctx->p.setValue(std::move(ctx->results));
+ delete ctx;
+ }
+ });
+ // template tail-recursion
+ whenAllVariadicHelper(ctx, std::forward<Fs>(tail)...);
+}
+
+template <typename T>
+struct WhenAllContext {
+ explicit WhenAllContext() : count(0), total(0) {}
+ Promise<std::vector<Try<T> > > p;
+ std::vector<Try<T> > results;
+ std::atomic<size_t> count;
+ size_t total;
+};
+
+template <typename T>
+struct WhenAnyContext {
+ explicit WhenAnyContext(size_t n) : done(false), ref_count(n) {};
+ Promise<std::pair<size_t, Try<T>>> p;
+ std::atomic<bool> done;
+ std::atomic<size_t> ref_count;
+ void decref() {
+ if (--ref_count == 0) {
+ delete this;
+ }
+ }
+};
+
+}}} // namespace