Unverified Commit 1b57112a authored by Dennis Jenkins's avatar Dennis Jenkins Committed by GitHub

Merge pull request #696 from dennisjenkins75/master

Ran clang-format on all headers (except date.h, since its not ours).
parents a6c8da47 352afa52
...@@ -9,1595 +9,1372 @@ ...@@ -9,1595 +9,1372 @@
#include <pistache/typeid.h> #include <pistache/typeid.h>
#include <type_traits> #include <atomic>
#include <condition_variable>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <atomic>
#include <vector>
#include <mutex> #include <mutex>
#include <condition_variable>
#include <stdexcept> #include <stdexcept>
#include <type_traits>
#include <typeinfo> #include <typeinfo>
#include <vector>
namespace Pistache { namespace Pistache {
namespace Async { namespace Async {
class Error : public std::runtime_error { class Error : public std::runtime_error {
public: public:
explicit Error(const char* what) : std::runtime_error(what) { } explicit Error(const char *what) : std::runtime_error(what) {}
explicit Error(const std::string& what) : std::runtime_error(what) { } explicit Error(const std::string &what) : std::runtime_error(what) {}
}; };
class BadType : public Error { class BadType : public Error {
public: public:
explicit BadType(TypeId id) explicit BadType(TypeId id)
: Error("Argument type can not be used to resolve the promise " : Error("Argument type can not be used to resolve the promise "
" (TypeId does not match)") " (TypeId does not match)"),
, id_(std::move(id)) id_(std::move(id)) {}
{ }
TypeId typeId() const { TypeId typeId() const { return id_; }
return id_;
}
private: private:
TypeId id_; TypeId id_;
}; };
class BadAnyCast : public std::bad_cast { class BadAnyCast : public std::bad_cast {
public: public:
const char* what() const noexcept override { return "Bad any cast"; } const char *what() const noexcept override { return "Bad any cast"; }
virtual ~BadAnyCast() { } virtual ~BadAnyCast() {}
}; };
enum class State { enum class State { Pending, Fulfilled, Rejected };
Pending, Fulfilled, Rejected
};
template<typename T> class Promise; template <typename T> class Promise;
class PromiseBase { class PromiseBase {
public: public:
virtual ~PromiseBase() { } virtual ~PromiseBase() {}
virtual bool isPending() const = 0; virtual bool isPending() const = 0;
virtual bool isFulfilled() const = 0; virtual bool isFulfilled() const = 0;
virtual bool isRejected() const = 0; virtual bool isRejected() const = 0;
bool isSettled() const { return isFulfilled() || isRejected(); } bool isSettled() const { return isFulfilled() || isRejected(); }
}; };
namespace detail { namespace detail {
template<typename Func, typename T> template <typename Func, typename T> struct IsCallable {
struct IsCallable {
template<typename U> template <typename U>
static auto test(U *) -> decltype(std::declval<Func>()(std::declval<U>()), std::true_type()); static auto test(U *)
-> decltype(std::declval<Func>()(std::declval<U>()), std::true_type());
template<typename U> template <typename U> static auto test(...) -> std::false_type;
static auto test(...) -> std::false_type;
static constexpr bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value; static constexpr bool value =
}; std::is_same<decltype(test<T>(0)), std::true_type>::value;
};
template<typename Func> template <typename Func>
struct IsMoveCallable : public IsMoveCallable<decltype(&Func::operator())> { }; struct IsMoveCallable : public IsMoveCallable<decltype(&Func::operator())> {};
template<typename R, typename Class, typename Arg> template <typename R, typename Class, typename Arg>
struct IsMoveCallable<R (Class::*)(Arg) const> : public std::is_rvalue_reference<Arg> { }; struct IsMoveCallable<R (Class::*)(Arg) const>
: public std::is_rvalue_reference<Arg> {};
template<typename Func, typename Arg> template <typename Func, typename Arg>
typename std::conditional< typename std::conditional<IsMoveCallable<Func>::value, Arg &&,
IsMoveCallable<Func>::value, const Arg &>::type
Arg&&, tryMove(Arg &arg) {
const Arg& return std::move(arg);
>::type tryMove(Arg& arg) { }
return std::move(arg);
} template <typename Func>
struct FunctionTrait : public FunctionTrait<decltype(&Func::operator())> {};
template <typename R, typename Class, typename... Args>
struct FunctionTrait<R (Class::*)(Args...) const> {
typedef R ReturnType;
static constexpr size_t ArgsCount = sizeof...(Args);
};
template <typename R, typename Class, typename... Args>
struct FunctionTrait<R (Class::*)(Args...)> {
typedef R ReturnType;
static constexpr size_t ArgsCount = sizeof...(Args);
};
template <typename T> struct RemovePromise { typedef T Type; };
template <typename T> struct RemovePromise<Promise<T>> { typedef T Type; };
template <size_t N, typename... T> struct nth_element;
template <typename Head, typename... Tail>
struct nth_element<0, Head, Tail...> {
typedef Head type;
};
template<typename Func> template <size_t N, typename Head, typename... Tail>
struct FunctionTrait : public FunctionTrait<decltype(&Func::operator())> { }; struct nth_element<N, Head, Tail...> {
typedef typename nth_element<N - 1, Tail...>::type type;
};
template<typename R, typename Class, typename... Args> } // namespace detail
struct FunctionTrait<R (Class::*)(Args...) const> {
typedef R ReturnType;
static constexpr size_t ArgsCount = sizeof...(Args); namespace Private {
};
template<typename R, typename Class, typename... Args> struct InternalRethrow {
struct FunctionTrait<R (Class::*)(Args...)> { explicit InternalRethrow(std::exception_ptr _exc) : exc(std::move(_exc)) {}
typedef R ReturnType;
static constexpr size_t ArgsCount = sizeof...(Args); std::exception_ptr exc;
}; };
template<typename T> struct IgnoreException {
struct RemovePromise { void operator()(std::exception_ptr) const {}
typedef T Type; };
};
template<typename T> struct NoExcept {
struct RemovePromise<Promise<T>> { void operator()(std::exception_ptr) const { std::terminate(); }
typedef T Type; };
};
template<size_t N, typename... T> struct nth_element; struct Throw {
void operator()(std::exception_ptr exc) const {
throw InternalRethrow(std::move(exc));
}
};
template<typename Head, typename... Tail> struct Core;
struct nth_element<0, Head, Tail...> {
typedef Head type;
};
template<size_t N, typename Head, typename... Tail> class Request {
struct nth_element<N, Head, Tail...> { public:
typedef typename nth_element<N - 1, Tail...>::type type; virtual void resolve(const std::shared_ptr<Core> &core) = 0;
}; virtual void reject(const std::shared_ptr<Core> &core) = 0;
virtual ~Request() {}
};
struct Core {
Core(State _state, TypeId _id)
: allocated(false), state(_state), exc(), mtx(), requests(), id(_id) {}
bool allocated;
std::atomic<State> state;
std::exception_ptr exc;
/*
* We need this lock because a Promise might be resolved or rejected from a
* thread A while a continuation to the same Promise (Core) might be attached
* at the same from a thread B. If that's the case, then we need to serialize
* operations so that we avoid a race-condition.
*
* Since we have a lock, we have a blocking progress guarantee but I don't
* expect this to be a major bottleneck as I don't expect major contention on
* the lock If it ends up being a bottlenick, try @improving it by
* experimenting with a lock-free scheme
*/
std::mutex mtx;
std::vector<std::shared_ptr<Request>> requests;
TypeId id;
virtual void *memory() = 0;
virtual bool isVoid() const = 0;
template <typename T, typename... Args> void construct(Args &&... args) {
if (isVoid())
throw Error("Can not construct a void core");
if (id != TypeId::of<T>()) {
throw BadType(id);
} }
namespace Private { void *mem = memory();
struct InternalRethrow { if (allocated) {
explicit InternalRethrow(std::exception_ptr _exc) reinterpret_cast<T *>(mem)->~T();
: exc(std::move(_exc)) allocated = false;
{ }
std::exception_ptr exc;
};
struct IgnoreException {
void operator()(std::exception_ptr) const { }
};
struct NoExcept {
void operator()(std::exception_ptr) const { std::terminate(); }
};
struct Throw {
void operator()(std::exception_ptr exc) const {
throw InternalRethrow(std::move(exc));
}
};
struct Core;
class Request {
public:
virtual void resolve(const std::shared_ptr<Core>& core) = 0;
virtual void reject(const std::shared_ptr<Core>& core) = 0;
virtual ~Request() {}
};
struct Core {
Core(State _state, TypeId _id)
: allocated(false)
, state(_state)
, exc()
, mtx()
, requests()
, id(_id)
{ }
bool allocated;
std::atomic<State> state;
std::exception_ptr exc;
/*
* We need this lock because a Promise might be resolved or rejected from a thread A
* while a continuation to the same Promise (Core) might be attached at the same from
* a thread B. If that's the case, then we need to serialize operations so that we
* avoid a race-condition.
*
* Since we have a lock, we have a blocking progress guarantee but I don't expect this
* to be a major bottleneck as I don't expect major contention on the lock
* If it ends up being a bottlenick, try @improving it by experimenting with a lock-free
* scheme
*/
std::mutex mtx;
std::vector<std::shared_ptr<Request>> requests;
TypeId id;
virtual void* memory() = 0;
virtual bool isVoid() const = 0;
template<typename T, typename... Args>
void construct(Args&&... args) {
if (isVoid())
throw Error("Can not construct a void core");
if (id != TypeId::of<T>()) {
throw BadType(id);
}
void *mem = memory();
if (allocated) {
reinterpret_cast<T*>(mem)->~T();
allocated = false;
}
new (mem) T(std::forward<Args>(args)...);
allocated = true;
state = State::Fulfilled;
}
virtual ~Core() {}
};
template<typename T>
struct CoreT : public Core {
CoreT()
: Core(State::Pending, TypeId::of<T>())
, storage()
{ }
~CoreT() {
if (allocated) {
reinterpret_cast<T*>(&storage)->~T();
allocated = false;
}
}
template<class Other>
struct Rebind {
typedef CoreT<Other> Type;
};
T& value() {
if (state != State::Fulfilled)
throw Error("Attempted to take the value of a not fulfilled promise");
return *reinterpret_cast<T*>(&storage);
}
bool isVoid() const override { return false; }
protected:
void *memory() override {
return &storage;
}
private:
typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type Storage;
Storage storage;
};
template<>
struct CoreT<void> : public Core {
CoreT()
: Core(State::Pending, TypeId::of<void>())
{ }
bool isVoid() const override { return true; }
protected:
void *memory() override {
return nullptr;
}
};
template<typename T>
struct Continuable : public Request {
explicit Continuable(const std::shared_ptr<Core>& chain)
: resolveCount_(0)
, rejectCount_(0)
, chain_(chain)
{ }
void resolve(const std::shared_ptr<Core>& core) override {
if (resolveCount_ >= 1)
return; //TODO is this the right thing?
//throw Error("Resolve must not be called more than once");
++resolveCount_;
doResolve(coreCast(core));
}
void reject(const std::shared_ptr<Core>& core) override {
if (rejectCount_ >= 1)
return; //TODO is this the right thing?
//throw Error("Reject must not be called more than once");
++rejectCount_;
try {
doReject(coreCast(core));
} catch (const InternalRethrow& e) {
chain_->exc = std::move(e.exc);
chain_->state = State::Rejected;
for (const auto& req: chain_->requests) {
req->reject(chain_);
}
}
}
std::shared_ptr<CoreT<T>> coreCast(const std::shared_ptr<Core>& core) const {
return std::static_pointer_cast<CoreT<T>>(core);
}
virtual void doResolve(const std::shared_ptr<CoreT<T>>& core) = 0;
virtual void doReject(const std::shared_ptr<CoreT<T>>& core) = 0;
virtual ~Continuable() { }
size_t resolveCount_;
size_t rejectCount_;
std::shared_ptr<Core> chain_;
};
namespace impl {
template<typename T, typename Resolve, typename Reject, typename U> struct Continuation;
template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const> : public Continuation<T, Resolve, Reject, Res (Args...)> {
typedef Continuation<T, Resolve, Reject, Res (Args...)> Base;
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Base(chain, std::move(resolve), std::move(reject))
{ }
};
template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)> : public Continuation<T, Resolve, Reject, Res (Args...)> {
typedef Continuation<T, Resolve, Reject, Res (Args...)> Base;
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Base(chain, std::move(resolve), std::move(reject))
{ }
};
// General specialization
template<typename T, typename Resolve, typename Reject, typename Res, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Args...)> : public Continuable<T> {
static_assert(sizeof...(Args) == 1,
"A continuation should only take one single argument");
typedef typename detail::nth_element<0, Args...>::type Arg;
static_assert(
std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Continuable<T>(chain)
, resolve_(resolve)
, reject_(reject)
{ }
void doResolve(const std::shared_ptr<CoreT<T>>& core) override {
finishResolve(resolve_(detail::tryMove<Resolve>(core->value())));
}
void doReject(const std::shared_ptr<CoreT<T>>& core) override {
reject_(core->exc);
for (const auto& req: this->chain_->requests) {
req->reject(this->chain_);
}
}
template<typename Ret>
void finishResolve(Ret&& ret) const {
typedef typename std::decay<Ret>::type CleanRet;
this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
for (const auto& req: this->chain_->requests) {
req->resolve(this->chain_);
}
}
Resolve resolve_;
Reject reject_;
};
// Specialization for a void-Promise
template<typename Resolve, typename Reject, typename Res, typename... Args>
struct Continuation<void, Resolve, Reject, Res (Args...)> : public Continuable<void> {
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Continuable<void>(chain)
, resolve_(resolve)
, reject_(reject)
{ }
static_assert(sizeof...(Args) == 0,
"Can not attach a non-void continuation to a void-Promise");
void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/) {
finishResolve(resolve_());
}
void doReject(const std::shared_ptr<CoreT<void>>& core) {
reject_(core->exc);
for (const auto& req: this->chain_->requests) {
req->reject(this->chain_);
}
}
template<typename Ret>
void finishResolve(Ret&& ret) const {
typedef typename std::remove_reference<Ret>::type CleanRet;
this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
for (const auto& req: this->chain_->requests) {
req->resolve(this->chain_);
}
}
Resolve resolve_;
Reject reject_;
};
// Specialization for a callback returning void
template<typename T, typename Resolve, typename Reject, typename... Args>
struct Continuation<T, Resolve, Reject, void (Args...)> : public Continuable<T> {
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Continuable<T>(chain)
, resolve_(resolve)
, reject_(reject)
{ }
static_assert(sizeof...(Args) == 1,
"A continuation should only take one single argument");
typedef typename detail::nth_element<0, Args...>::type Arg;
static_assert(
std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
void doResolve(const std::shared_ptr<CoreT<T>>& core) override {
resolve_(core->value());
}
void doReject(const std::shared_ptr<CoreT<T>>& core) override {
reject_(core->exc);
}
Resolve resolve_;
Reject reject_;
};
// Specialization for a void-Promise on a callback returning void
template<typename Resolve, typename Reject, typename... Args>
struct Continuation<void, Resolve, Reject, void (Args...)> : public Continuable<void> {
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Continuable<void>(chain)
, resolve_(resolve)
, reject_(reject)
{ }
static_assert(sizeof...(Args) == 0,
"Can not attach a non-void continuation to a void-Promise");
void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/) override {
resolve_();
}
void doReject(const std::shared_ptr<CoreT<void>>& core) override {
reject_(core->exc);
}
Resolve resolve_;
Reject reject_;
};
// Specialization for a callback returning a Promise
template<typename T, typename Resolve, typename Reject, typename U, typename... Args>
struct Continuation<T, Resolve, Reject, Promise<U> (Args...)> : public Continuable<T> {
static_assert(sizeof...(Args) == 1,
"A continuation should only take one single argument");
typedef typename detail::nth_element<0, Args...>::type Arg;
static_assert(
std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Continuable<T>(chain)
, resolve_(resolve)
, reject_(reject)
{ }
void doResolve(const std::shared_ptr<CoreT<T>>& core) override {
auto promise = resolve_(detail::tryMove<Resolve>(core->value()));
finishResolve(promise);
}
void doReject(const std::shared_ptr<CoreT<T>>& core) override {
reject_(core->exc);
for (const auto& req: core->requests) {
req->reject(core);
}
}
template<typename PromiseType>
struct Chainer {
explicit Chainer(const std::shared_ptr<Private::Core>& core)
: chainCore(core)
{ }
void operator()(const PromiseType& val) {
chainCore->construct<PromiseType>(val);
for (const auto& req: chainCore->requests) {
req->resolve(chainCore);
}
}
std::shared_ptr<Core> chainCore;
};
template<
typename Promise,
typename Type = typename detail::RemovePromise<Promise>::Type>
Chainer<Type>
makeChainer(const Promise&) {
return Chainer<Type>(this->chain_);
}
template<typename P>
void finishResolve(P& promise) {
auto chainer = makeChainer(promise);
std::weak_ptr<Core> weakPtr = this->chain_;
promise.then(std::move(chainer), [weakPtr](std::exception_ptr exc) {
if (auto core = weakPtr.lock()) {
core->exc = std::move(exc);
core->state = State::Rejected;
for (const auto& req: core->requests) {
req->reject(core);
}
}
});
}
Resolve resolve_;
Reject reject_;
};
// Specialization for a void callback returning a Promise
template<typename Resolve, typename Reject, typename U, typename... Args>
struct Continuation<void, Resolve, Reject, Promise<U> (Args...)> : public Continuable<void> {
static_assert(sizeof...(Args) == 0,
"Can not attach a non-void continuation to a void-Promise");
Continuation(
const std::shared_ptr<Core>& chain,
Resolve resolve,
Reject reject)
: Continuable<void>(chain)
, resolve_(resolve)
, reject_(reject)
{ }
void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/) {
auto promise = resolve_();
finishResolve(promise);
}
void doReject(const std::shared_ptr<CoreT<void>>& core) {
reject_(core->exc);
for (const auto& req: core->requests) {
req->reject(core);
}
}
template<typename PromiseType, typename Dummy = void>
struct Chainer {
explicit Chainer(const std::shared_ptr<Private::Core>& core)
: chainCore(core)
{ }
void operator()(const PromiseType& val) {
chainCore->construct<PromiseType>(val);
for (const auto& req: chainCore->requests) {
req->resolve(chainCore);
}
}
std::shared_ptr<Core> chainCore;
};
template<typename Dummy>
struct Chainer<void, Dummy> {
explicit Chainer(const std::shared_ptr<Private::Core>& core)
: chainCore(core)
{ }
void operator()() {
auto core = this->chain_;
core->state = State::Fulfilled;
for (const auto& req: chainCore->requests) {
req->resolve(chainCore);
}
}
std::shared_ptr<Core> chainCore;
};
template<
typename Promise,
typename Type = typename detail::RemovePromise<Promise>::Type>
Chainer<Type>
makeChainer(const Promise&) {
return Chainer<Type>(this->chain_);
}
template<typename P>
void finishResolve(P& promise) {
auto chainer = makeChainer(promise);
promise.then(std::move(chainer), [=](std::exception_ptr exc) {
auto core = this->chain_;
core->exc = std::move(exc);
core->state = State::Rejected;
for (const auto& req: core->requests) {
req->reject(core);
}
});
}
Resolve resolve_;
Reject reject_;
};
} // namespace impl
template<typename T, typename Resolve, typename Reject, typename Sig>
struct Continuation : public impl::Continuation<T, Resolve, Reject, decltype(&Sig::operator())>
{
typedef impl::Continuation<T, Resolve, Reject, decltype(&Sig::operator())> Base;
Continuation(
const std::shared_ptr<Core>& core,
Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject))
{ }
};
template<typename T, typename Resolve, typename Reject, typename Res, typename... Args>
struct Continuation<T, Resolve, Reject, Res (*)(Args...)> : public impl::Continuation<T, Resolve, Reject, Res (Args...)>
{
typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
Continuation(
const std::shared_ptr<Core>& core,
Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject))
{ }
};
template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)> : public impl::Continuation<T, Resolve, Reject, Res (Args...)>
{
typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
Continuation(
const std::shared_ptr<Core>& core,
Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject))
{ }
};
template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const> : public impl::Continuation<T, Resolve, Reject, Res (Args...)>
{
typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
Continuation(
const std::shared_ptr<Core>& core,
Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject))
{ }
};
template<typename T, typename Resolve, typename Reject, typename Res, typename... Args>
struct Continuation<T, Resolve, Reject, std::function<Res (Args...)>> : public impl::Continuation<T, Resolve, Reject, Res (Args...)> {
typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
Continuation(
const std::shared_ptr<Core>& core,
Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject))
{ }
};
} }
class Resolver { new (mem) T(std::forward<Args>(args)...);
public: allocated = true;
state = State::Fulfilled;
}
explicit Resolver(const std::shared_ptr<Private::Core> &core) virtual ~Core() {}
: core_(core) };
{ }
Resolver(const Resolver& other) = delete; template <typename T> struct CoreT : public Core {
Resolver& operator=(const Resolver& other) = delete; CoreT() : Core(State::Pending, TypeId::of<T>()), storage() {}
Resolver(Resolver&& other) = default; ~CoreT() {
Resolver& operator=(Resolver&& other) = default; if (allocated) {
reinterpret_cast<T *>(&storage)->~T();
allocated = false;
}
}
template<typename Arg> template <class Other> struct Rebind { typedef CoreT<Other> Type; };
bool operator()(Arg&& arg) const {
if (!core_) return false;
typedef typename std::remove_reference<Arg>::type Type; T &value() {
if (state != State::Fulfilled)
throw Error("Attempted to take the value of a not fulfilled promise");
if (core_->state != State::Pending) return *reinterpret_cast<T *>(&storage);
throw Error("Attempt to resolve a fulfilled promise"); }
/* In a ideal world, this should be checked at compile-time rather bool isVoid() const override { return false; }
* than runtime. However, since types are erased, this looks like
* a difficult task
*/
if (core_->isVoid()) {
throw Error("Attempt to resolve a void promise with arguments");
}
std::unique_lock<std::mutex> guard(core_->mtx); protected:
core_->construct<Type>(std::forward<Arg>(arg)); void *memory() override { return &storage; }
for (const auto& req: core_->requests) { private:
req->resolve(core_); typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type Storage;
} Storage storage;
};
return true; template <> struct CoreT<void> : public Core {
} CoreT() : Core(State::Pending, TypeId::of<void>()) {}
bool operator()() const { bool isVoid() const override { return true; }
if (!core_) return false;
if (core_->state != State::Pending) protected:
throw Error("Attempt to resolve a fulfilled promise"); void *memory() override { return nullptr; }
};
if (!core_->isVoid()) template <typename T> struct Continuable : public Request {
throw Error("Attempt ro resolve a non-void promise with no argument"); explicit Continuable(const std::shared_ptr<Core> &chain)
: resolveCount_(0), rejectCount_(0), chain_(chain) {}
std::unique_lock<std::mutex> guard(core_->mtx); void resolve(const std::shared_ptr<Core> &core) override {
core_->state = State::Fulfilled; if (resolveCount_ >= 1)
for (const auto& req: core_->requests) { return; // TODO is this the right thing?
req->resolve(core_); // throw Error("Resolve must not be called more than once");
}
return true; ++resolveCount_;
} doResolve(coreCast(core));
}
void clear() { void reject(const std::shared_ptr<Core> &core) override {
core_ = nullptr; if (rejectCount_ >= 1)
} return; // TODO is this the right thing?
// throw Error("Reject must not be called more than once");
Resolver clone() { ++rejectCount_;
return Resolver(core_); try {
} doReject(coreCast(core));
} catch (const InternalRethrow &e) {
chain_->exc = std::move(e.exc);
chain_->state = State::Rejected;
for (const auto &req : chain_->requests) {
req->reject(chain_);
}
}
}
private: std::shared_ptr<CoreT<T>> coreCast(const std::shared_ptr<Core> &core) const {
std::shared_ptr<Private::Core> core_; return std::static_pointer_cast<CoreT<T>>(core);
}; }
class Rejection { virtual void doResolve(const std::shared_ptr<CoreT<T>> &core) = 0;
public: virtual void doReject(const std::shared_ptr<CoreT<T>> &core) = 0;
explicit Rejection(const std::shared_ptr<Private::Core>& core) virtual ~Continuable() {}
: core_(core)
{ }
Rejection(const Rejection& other) = delete; size_t resolveCount_;
Rejection& operator=(const Rejection& other) = delete; size_t rejectCount_;
std::shared_ptr<Core> chain_;
};
Rejection(Rejection&& other) = default; namespace impl {
Rejection& operator=(Rejection&& other) = default;
template<typename Exc> template <typename T, typename Resolve, typename Reject, typename U>
bool operator()(Exc exc) const { struct Continuation;
if (!core_) return false;
if (core_->state != State::Pending) template <typename T, typename Resolve, typename Reject, typename Res,
throw Error("Attempt to reject a fulfilled promise"); typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const>
: public Continuation<T, Resolve, Reject, Res(Args...)> {
typedef Continuation<T, Resolve, Reject, Res(Args...)> Base;
std::unique_lock<std::mutex> guard(core_->mtx); Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
core_->exc = std::make_exception_ptr(exc); Reject reject)
core_->state = State::Rejected; : Base(chain, std::move(resolve), std::move(reject)) {}
for (const auto& req: core_->requests) { };
req->reject(core_);
}
return true; template <typename T, typename Resolve, typename Reject, typename Res,
} typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)>
: public Continuation<T, Resolve, Reject, Res(Args...)> {
typedef Continuation<T, Resolve, Reject, Res(Args...)> Base;
void clear() { Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
core_ = nullptr; Reject reject)
} : Base(chain, std::move(resolve), std::move(reject)) {}
};
Rejection clone() { // General specialization
return Rejection(core_); template <typename T, typename Resolve, typename Reject, typename Res,
} typename... Args>
struct Continuation<T, Resolve, Reject, Res(Args...)> : public Continuable<T> {
private: static_assert(sizeof...(Args) == 1,
std::shared_ptr<Private::Core> core_; "A continuation should only take one single argument");
}; typedef typename detail::nth_element<0, Args...>::type Arg;
template<typename T> static_assert(std::is_same<T, Arg>::value ||
class Deferred { std::is_convertible<T, Arg>::value,
public: "Incompatible types detected");
Deferred() Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
: resolver(nullptr) Reject reject)
, rejection(nullptr) : Continuable<T>(chain), resolve_(resolve), reject_(reject) {}
{ }
Deferred(const Deferred& other) = delete; void doResolve(const std::shared_ptr<CoreT<T>> &core) override {
Deferred& operator=(const Deferred& other) = delete; finishResolve(resolve_(detail::tryMove<Resolve>(core->value())));
}
Deferred(Deferred&& other) = default; void doReject(const std::shared_ptr<CoreT<T>> &core) override {
Deferred& operator=(Deferred&& other) = default; reject_(core->exc);
for (const auto &req : this->chain_->requests) {
req->reject(this->chain_);
}
}
Deferred(Resolver _resolver, Rejection _reject) template <typename Ret> void finishResolve(Ret &&ret) const {
: resolver(std::move(_resolver)) typedef typename std::decay<Ret>::type CleanRet;
, rejection(std::move(_reject)) this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
{ } for (const auto &req : this->chain_->requests) {
req->resolve(this->chain_);
}
}
template<typename U> Resolve resolve_;
bool resolve(U&& arg) { Reject reject_;
typedef typename std::remove_reference<U>::type CleanU; };
static_assert(std::is_same<T, CleanU>::value || std::is_convertible<U, T>::value, // Specialization for a void-Promise
"Types mismatch"); template <typename Resolve, typename Reject, typename Res, typename... Args>
struct Continuation<void, Resolve, Reject, Res(Args...)>
: public Continuable<void> {
return resolver(std::forward<U>(arg)); Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
} Reject reject)
: Continuable<void>(chain), resolve_(resolve), reject_(reject) {}
template<typename... Args> static_assert(sizeof...(Args) == 0,
void emplaceResolve(Args&& ...) { "Can not attach a non-void continuation to a void-Promise");
}
template<typename Exc> void doResolve(const std::shared_ptr<CoreT<void>> & /*core*/) {
bool reject(Exc exc) { finishResolve(resolve_());
return rejection(std::move(exc)); }
}
void clear() { void doReject(const std::shared_ptr<CoreT<void>> &core) {
resolver.clear(); reject_(core->exc);
rejection.clear(); for (const auto &req : this->chain_->requests) {
} req->reject(this->chain_);
}
}
private: template <typename Ret> void finishResolve(Ret &&ret) const {
Resolver resolver; typedef typename std::remove_reference<Ret>::type CleanRet;
Rejection rejection; this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
}; for (const auto &req : this->chain_->requests) {
req->resolve(this->chain_);
}
}
template<> Resolve resolve_;
class Deferred<void> { Reject reject_;
public: };
Deferred() // Specialization for a callback returning void
: resolver(nullptr) template <typename T, typename Resolve, typename Reject, typename... Args>
, rejection(nullptr) struct Continuation<T, Resolve, Reject, void(Args...)> : public Continuable<T> {
{ }
Deferred(const Deferred& other) = delete; Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Deferred& operator=(const Deferred& other) = delete; Reject reject)
: Continuable<T>(chain), resolve_(resolve), reject_(reject) {}
Deferred(Deferred&& other) = default; static_assert(sizeof...(Args) == 1,
Deferred& operator=(Deferred&& other) = default; "A continuation should only take one single argument");
Deferred(Resolver _resolver, Rejection _reject) typedef typename detail::nth_element<0, Args...>::type Arg;
: resolver(std::move(_resolver))
, rejection(std::move(_reject))
{ }
void resolve() { static_assert(std::is_same<T, Arg>::value ||
resolver(); std::is_convertible<T, Arg>::value,
} "Incompatible types detected");
template<typename Exc> void doResolve(const std::shared_ptr<CoreT<T>> &core) override {
void reject(Exc _exc) { resolve_(core->value());
rejection(std::move(_exc)); }
}
private: void doReject(const std::shared_ptr<CoreT<T>> &core) override {
Resolver resolver; reject_(core->exc);
Rejection rejection; }
};
static constexpr Private::IgnoreException IgnoreException{}; Resolve resolve_;
static constexpr Private::NoExcept NoExcept{}; Reject reject_;
static constexpr Private::Throw Throw{}; };
namespace details {
/*
* Note that we could use std::result_of to SFINAE-out and dispatch to the right call
* However, gcc 4.7 does not correctly support std::result_of for SFINAE purposes, so we
* use a decltype SFINAE-expression instead.
*
* See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3462.html and
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56283 for reference
*/
template<typename T, typename Func>
auto callAsync(Func func, Resolver& resolver, Rejection& rejection)
-> decltype(std::declval<Func>()(resolver, rejection), void()) {
func(resolver, rejection);
}
template<typename T, typename Func> // Specialization for a void-Promise on a callback returning void
auto callAsync(Func func, Resolver& resolver, Rejection& rejection) template <typename Resolve, typename Reject, typename... Args>
-> decltype(std::declval<Func>()(Deferred<T>()), void()) { struct Continuation<void, Resolve, Reject, void(Args...)>
func(Deferred<T>(std::move(resolver), std::move(rejection))); : public Continuable<void> {
}
}
template<typename T>
class Promise : public PromiseBase
{
public:
template<typename U> friend class Promise;
typedef Private::CoreT<T> Core;
template<typename Func>
explicit Promise(Func func)
: core_(std::make_shared<Core>())
, resolver_(core_)
, rejection_(core_)
{
details::callAsync<T>(func, resolver_, rejection_);
}
Promise(const Promise<T>& other) = delete; Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Promise& operator=(const Promise<T>& other) = delete; Reject reject)
: Continuable<void>(chain), resolve_(resolve), reject_(reject) {}
Promise(Promise<T>&& other) = default; static_assert(sizeof...(Args) == 0,
Promise& operator=(Promise<T>&& other) = default; "Can not attach a non-void continuation to a void-Promise");
virtual ~Promise() void doResolve(const std::shared_ptr<CoreT<void>> & /*core*/) override {
{ resolve_();
}
} void doReject(const std::shared_ptr<CoreT<void>> &core) override {
reject_(core->exc);
}
template<typename U> Resolve resolve_;
static Reject reject_;
Promise<T> };
resolved(U&& value) {
static_assert(!std::is_void<T>::value,
"Can not resolve a void promise with parameters");
static_assert(std::is_same<T, U>::value || std::is_convertible<U, T>::value,
"Incompatible value type");
auto core = std::make_shared<Core>();
core->template construct<T>(std::forward<U>(value));
return Promise<T>(std::move(core));
}
static // Specialization for a callback returning a Promise
Promise<void> template <typename T, typename Resolve, typename Reject, typename U,
resolved() { typename... Args>
static_assert(std::is_void<T>::value, struct Continuation<T, Resolve, Reject, Promise<U>(Args...)>
"Resolving a non-void promise requires parameters"); : public Continuable<T> {
auto core = std::make_shared<Core>(); static_assert(sizeof...(Args) == 1,
core->state = State::Fulfilled; "A continuation should only take one single argument");
return Promise<T>(std::move(core));
} typedef typename detail::nth_element<0, Args...>::type Arg;
static_assert(std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Reject reject)
: Continuable<T>(chain), resolve_(resolve), reject_(reject) {}
void doResolve(const std::shared_ptr<CoreT<T>> &core) override {
auto promise = resolve_(detail::tryMove<Resolve>(core->value()));
finishResolve(promise);
}
void doReject(const std::shared_ptr<CoreT<T>> &core) override {
reject_(core->exc);
for (const auto &req : core->requests) {
req->reject(core);
}
}
template <typename PromiseType> struct Chainer {
explicit Chainer(const std::shared_ptr<Private::Core> &core)
: chainCore(core) {}
void operator()(const PromiseType &val) {
chainCore->construct<PromiseType>(val);
for (const auto &req : chainCore->requests) {
req->resolve(chainCore);
}
}
template<typename Exc> std::shared_ptr<Core> chainCore;
static Promise<T> };
rejected(Exc exc) {
auto core = std::make_shared<Core>(); template <typename Promise,
core->exc = std::make_exception_ptr(exc); typename Type = typename detail::RemovePromise<Promise>::Type>
core->state = State::Rejected; Chainer<Type> makeChainer(const Promise &) {
return Promise<T>(std::move(core)); return Chainer<Type>(this->chain_);
}
template <typename P> void finishResolve(P &promise) {
auto chainer = makeChainer(promise);
std::weak_ptr<Core> weakPtr = this->chain_;
promise.then(std::move(chainer), [weakPtr](std::exception_ptr exc) {
if (auto core = weakPtr.lock()) {
core->exc = std::move(exc);
core->state = State::Rejected;
for (const auto &req : core->requests) {
req->reject(core);
} }
}
});
}
Resolve resolve_;
Reject reject_;
};
// Specialization for a void callback returning a Promise
template <typename Resolve, typename Reject, typename U, typename... Args>
struct Continuation<void, Resolve, Reject, Promise<U>(Args...)>
: public Continuable<void> {
static_assert(sizeof...(Args) == 0,
"Can not attach a non-void continuation to a void-Promise");
Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Reject reject)
: Continuable<void>(chain), resolve_(resolve), reject_(reject) {}
void doResolve(const std::shared_ptr<CoreT<void>> & /*core*/) {
auto promise = resolve_();
finishResolve(promise);
}
void doReject(const std::shared_ptr<CoreT<void>> &core) {
reject_(core->exc);
for (const auto &req : core->requests) {
req->reject(core);
}
}
bool isPending() const override { return core_->state == State::Pending; } template <typename PromiseType, typename Dummy = void> struct Chainer {
bool isFulfilled() const override { return core_->state == State::Fulfilled; } explicit Chainer(const std::shared_ptr<Private::Core> &core)
bool isRejected() const override { return core_->state == State::Rejected; } : chainCore(core) {}
template<typename ResolveFunc, typename RejectFunc> void operator()(const PromiseType &val) {
auto chainCore->construct<PromiseType>(val);
then(ResolveFunc resolveFunc, RejectFunc rejectFunc) for (const auto &req : chainCore->requests) {
-> Promise< req->resolve(chainCore);
typename detail::RemovePromise< }
typename detail::FunctionTrait<ResolveFunc>::ReturnType }
>::Type
>
{
typedef typename detail::RemovePromise< std::shared_ptr<Core> chainCore;
typename detail::FunctionTrait<ResolveFunc>::ReturnType };
>::Type RetType;
Promise<RetType> promise; template <typename Dummy> struct Chainer<void, Dummy> {
explicit Chainer(const std::shared_ptr<Private::Core> &core)
: chainCore(core) {}
typedef Private::Continuation<T, ResolveFunc, RejectFunc, ResolveFunc> Continuation; void operator()() {
std::shared_ptr<Private::Request> req = std::make_shared<Continuation>(promise.core_, resolveFunc, rejectFunc); auto core = this->chain_;
core->state = State::Fulfilled;
std::unique_lock<std::mutex> guard(core_->mtx); for (const auto &req : chainCore->requests) {
if (isFulfilled()) { req->resolve(chainCore);
req->resolve(core_); }
} }
else if (isRejected()) {
req->reject(core_);
}
core_->requests.push_back(req); std::shared_ptr<Core> chainCore;
};
return promise; template <typename Promise,
} typename Type = typename detail::RemovePromise<Promise>::Type>
Chainer<Type> makeChainer(const Promise &) {
return Chainer<Type>(this->chain_);
}
private: template <typename P> void finishResolve(P &promise) {
Promise() auto chainer = makeChainer(promise);
: core_(std::make_shared<Core>()) promise.then(std::move(chainer), [=](std::exception_ptr exc) {
, resolver_(core_) auto core = this->chain_;
, rejection_(core_) core->exc = std::move(exc);
{ core->state = State::Rejected;
}
explicit Promise(std::shared_ptr<Core>&& core) for (const auto &req : core->requests) {
: core_(core) req->reject(core);
, resolver_(core_) }
, rejection_(core_) });
{ } }
Resolve resolve_;
Reject reject_;
};
} // namespace impl
template <typename T, typename Resolve, typename Reject, typename Sig>
struct Continuation : public impl::Continuation<T, Resolve, Reject,
decltype(&Sig::operator())> {
typedef impl::Continuation<T, Resolve, Reject, decltype(&Sig::operator())>
Base;
Continuation(const std::shared_ptr<Core> &core, Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject)) {}
};
template <typename T, typename Resolve, typename Reject, typename Res,
typename... Args>
struct Continuation<T, Resolve, Reject, Res (*)(Args...)>
: public impl::Continuation<T, Resolve, Reject, Res(Args...)> {
typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;
Continuation(const std::shared_ptr<Core> &core, Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject)) {}
};
template <typename T, typename Resolve, typename Reject, typename Res,
typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)>
: public impl::Continuation<T, Resolve, Reject, Res(Args...)> {
typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;
Continuation(const std::shared_ptr<Core> &core, Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject)) {}
};
template <typename T, typename Resolve, typename Reject, typename Res,
typename Cls, typename... Args>
struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const>
: public impl::Continuation<T, Resolve, Reject, Res(Args...)> {
typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;
Continuation(const std::shared_ptr<Core> &core, Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject)) {}
};
template <typename T, typename Resolve, typename Reject, typename Res,
typename... Args>
struct Continuation<T, Resolve, Reject, std::function<Res(Args...)>>
: public impl::Continuation<T, Resolve, Reject, Res(Args...)> {
typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;
Continuation(const std::shared_ptr<Core> &core, Resolve resolve,
Reject reject)
: Base(core, std::move(resolve), std::move(reject)) {}
};
} // namespace Private
class Resolver {
public:
explicit Resolver(const std::shared_ptr<Private::Core> &core) : core_(core) {}
Resolver(const Resolver &other) = delete;
Resolver &operator=(const Resolver &other) = delete;
Resolver(Resolver &&other) = default;
Resolver &operator=(Resolver &&other) = default;
template <typename Arg> bool operator()(Arg &&arg) const {
if (!core_)
return false;
typedef typename std::remove_reference<Arg>::type Type;
if (core_->state != State::Pending)
throw Error("Attempt to resolve a fulfilled promise");
/* In a ideal world, this should be checked at compile-time rather
* than runtime. However, since types are erased, this looks like
* a difficult task
*/
if (core_->isVoid()) {
throw Error("Attempt to resolve a void promise with arguments");
}
std::shared_ptr<Core> core_; std::unique_lock<std::mutex> guard(core_->mtx);
Resolver resolver_; core_->construct<Type>(std::forward<Arg>(arg));
Rejection rejection_;
};
template<typename T> for (const auto &req : core_->requests) {
class Barrier { req->resolve(core_);
public: }
explicit Barrier(Promise<T>& promise)
: promise_(promise) {
}
void wait() { return true;
if (promise_.isFulfilled() || promise_.isRejected()) return; }
promise_.then([&](const T&) mutable { bool operator()() const {
std::unique_lock<std::mutex> guard(mtx); if (!core_)
cv.notify_one(); return false;
}, [&](std::exception_ptr) mutable {
std::unique_lock<std::mutex> guard(mtx);
cv.notify_one();
});
std::unique_lock<std::mutex> guard(mtx); if (core_->state != State::Pending)
cv.wait(guard, [&] { return promise_.isFulfilled() || promise_.isRejected(); }); throw Error("Attempt to resolve a fulfilled promise");
}
template<class Rep, class Period> if (!core_->isVoid())
std::cv_status wait_for(const std::chrono::duration<Rep, Period>& period) { throw Error("Attempt ro resolve a non-void promise with no argument");
if (promise_.isFulfilled() || promise_.isRejected()) return std::cv_status::no_timeout;
promise_.then([&](const T&) mutable { std::unique_lock<std::mutex> guard(core_->mtx);
std::unique_lock<std::mutex> guard(mtx); core_->state = State::Fulfilled;
cv.notify_one(); for (const auto &req : core_->requests) {
}, [&](std::exception_ptr) mutable { req->resolve(core_);
std::unique_lock<std::mutex> guard(mtx); }
cv.notify_one();
});
std::unique_lock<std::mutex> guard(mtx); return true;
return cv.wait_for(guard, period); }
}
private: void clear() { core_ = nullptr; }
Promise<T>& promise_;
mutable std::mutex mtx; Resolver clone() { return Resolver(core_); }
std::condition_variable cv;
}; private:
std::shared_ptr<Private::Core> core_;
};
namespace Impl { class Rejection {
struct Any; public:
explicit Rejection(const std::shared_ptr<Private::Core> &core)
: core_(core) {}
Rejection(const Rejection &other) = delete;
Rejection &operator=(const Rejection &other) = delete;
Rejection(Rejection &&other) = default;
Rejection &operator=(Rejection &&other) = default;
template <typename Exc> bool operator()(Exc exc) const {
if (!core_)
return false;
if (core_->state != State::Pending)
throw Error("Attempt to reject a fulfilled promise");
std::unique_lock<std::mutex> guard(core_->mtx);
core_->exc = std::make_exception_ptr(exc);
core_->state = State::Rejected;
for (const auto &req : core_->requests) {
req->reject(core_);
} }
class Any { return true;
public: }
friend struct Impl::Any;
Any(const Any& other) = default; void clear() { core_ = nullptr; }
Any& operator=(const Any& other) = default;
Any(Any&& other) = default; Rejection clone() { return Rejection(core_); }
Any& operator=(Any&& other) = default;
template<typename T> private:
bool is() const { std::shared_ptr<Private::Core> core_;
return core_->id == TypeId::of<T>(); };
}
template<typename T> template <typename T> class Deferred {
T cast() const { public:
if (!is<T>()) throw BadAnyCast(); Deferred() : resolver(nullptr), rejection(nullptr) {}
auto core = std::static_pointer_cast<Private::CoreT<T>>(core_); Deferred(const Deferred &other) = delete;
return core->value(); Deferred &operator=(const Deferred &other) = delete;
}
private: Deferred(Deferred &&other) = default;
explicit Any(const std::shared_ptr<Private::Core>& core) Deferred &operator=(Deferred &&other) = default;
: core_(core)
{ }
std::shared_ptr<Private::Core> core_;
}; Deferred(Resolver _resolver, Rejection _reject)
: resolver(std::move(_resolver)), rejection(std::move(_reject)) {}
template <typename U> bool resolve(U &&arg) {
typedef typename std::remove_reference<U>::type CleanU;
static_assert(std::is_same<T, CleanU>::value ||
std::is_convertible<U, T>::value,
"Types mismatch");
return resolver(std::forward<U>(arg));
}
template <typename... Args> void emplaceResolve(Args &&...) {}
template <typename Exc> bool reject(Exc exc) {
return rejection(std::move(exc));
}
void clear() {
resolver.clear();
rejection.clear();
}
private:
Resolver resolver;
Rejection rejection;
};
template <> class Deferred<void> {
public:
Deferred() : resolver(nullptr), rejection(nullptr) {}
Deferred(const Deferred &other) = delete;
Deferred &operator=(const Deferred &other) = delete;
Deferred(Deferred &&other) = default;
Deferred &operator=(Deferred &&other) = default;
namespace Impl { Deferred(Resolver _resolver, Rejection _reject)
: resolver(std::move(_resolver)), rejection(std::move(_reject)) {}
/* Instead of duplicating the code between whenAll and whenAny functions, the main implementation void resolve() { resolver(); }
* is in the When class below and we configure the class with a policy instead, depending if we
* are executing an "all" or "any" operation, how cool is that ?
*/
struct All {
struct Data { template <typename Exc> void reject(Exc _exc) { rejection(std::move(_exc)); }
Data(const size_t _total, Resolver _resolver, Rejection _rejection)
: total(_total)
, resolved(0)
, rejected(false)
, mtx()
, resolve(std::move(_resolver))
, reject(std::move(_rejection))
{ }
const size_t total; private:
size_t resolved; Resolver resolver;
bool rejected; Rejection rejection;
std::mutex mtx; };
Resolver resolve;
Rejection reject;
};
template<size_t Index, typename T, typename Data>
static void resolveT(const T& val, Data& data) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected) return;
// @Check thread-safety of std::get ?
std::get<Index>(data->results) = val;
data->resolved++;
if (data->resolved == data->total) {
data->resolve(data->results);
}
}
template<typename Data>
static void resolveVoid(Data& data) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected) return;
data->resolved++;
if (data->resolved == data->total) {
data->resolve(data->results);
}
}
template<typename Data>
static void reject(std::exception_ptr exc, Data& data) {
std::lock_guard<std::mutex> guard(data->mtx);
data->rejected = true;
data->reject(exc);
}
};
struct Any {
struct Data {
Data(size_t, Resolver resolver, Rejection rejection)
: done(false)
, mtx()
, resolve(std::move(resolver))
, reject(std::move(rejection))
{ }
bool done;
std::mutex mtx;
Resolver resolve;
Rejection reject;
};
template<size_t Index, typename T, typename Data>
static void resolveT(const T& val, Data& data) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->done) return;
// Instead of allocating a new core, ideally we could share the same core as
// the relevant promise but we do not have access to the promise here is so meh
auto core = std::make_shared<Private::CoreT<T>>();
core->template construct<T>(val);
data->resolve(Async::Any(core));
data->done = true;
}
template<typename Data>
static void resolveVoid(Data& data) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->done) return;
auto core = std::make_shared<Private::CoreT<void>>();
data->resolve(Async::Any(core));
data->done = true;
}
template<typename Data>
static void reject(std::exception_ptr exc, Data& data) {
std::lock_guard<std::mutex> guard(data->mtx);
data->done = true;
data->reject(exc);
}
};
template<typename ContinuationPolicy>
struct When {
When(Resolver resolver, Rejection rejection)
: resolve(std::move(resolver))
, reject(std::move(rejection))
{ }
template<typename... Args>
void operator()(Args&&... args) {
whenArgs(std::forward<Args>(args)...);
}
private:
template<typename T, size_t Index, typename Data>
struct WhenContinuation {
explicit WhenContinuation(Data _data)
: data(std::move(_data))
{ }
void operator()(const T& val) const {
ContinuationPolicy::template resolveT<Index>(val, data);
}
Data data;
};
template<size_t Index, typename Data>
struct WhenContinuation<void, Index, Data> {
explicit WhenContinuation(Data _data)
: data(std::move(_data))
{ }
void operator()() const {
ContinuationPolicy::resolveVoid(data);
}
Data data;
};
template<typename T, size_t Index, typename Data>
WhenContinuation<T, Index, Data>
makeContinuation(const Data& data) {
return WhenContinuation<T, Index, Data>(data);
}
template<size_t Index, typename Data, typename T>
void when(const Data& data, Promise<T>& promise) {
promise.then(makeContinuation<T, Index>(data), [=](std::exception_ptr ptr) {
ContinuationPolicy::reject(std::move(ptr), data);
});
}
template<size_t Index, typename Data, typename T>
void when(const Data& data, T&& arg) {
typedef typename std::remove_reference<T>::type CleanT;
auto promise = Promise<CleanT>::resolved(std::forward<T>(arg));
when<Index>(data, promise);
}
template<typename... Args>
void whenArgs(Args&& ...args) {
typedef std::tuple<
typename detail::RemovePromise<
typename std::remove_reference<Args>::type
>::Type...
> Results;
/* We need to keep the results alive until the last promise
* finishes its execution
*/
/* See the trick here ? Basically, we only have access to the real type of the results
* in this function. The policy classes do not have access to the full type (std::tuple),
* but, instead, take a generic template data type as a parameter. They only need to know
* that results is a tuple, they do not need to know the real type of the results.
*
* This is some sort of compile-time template type-erasing, hue
*/
struct Data : public ContinuationPolicy::Data {
Data(size_t total, Resolver resolver, Rejection rejection)
: ContinuationPolicy::Data(total, std::move(resolver), std::move(rejection))
{ }
Results results;
};
auto data = std::make_shared<Data>(sizeof...(Args), std::move(resolve), std::move(reject));
whenArgs<0>(data, std::forward<Args>(args)...);
}
template<size_t Index, typename Data, typename Head, typename... Rest>
void whenArgs(const Data& data, Head&& head, Rest&& ...rest) {
when<Index>(data, std::forward<Head>(head));
whenArgs<Index + 1>(data, std::forward<Rest>(rest)...);
}
template<size_t Index, typename Data, typename Head>
void whenArgs(const Data& data, Head&& head) {
when<Index>(data, std::forward<Head>(head));
}
Resolver resolve;
Rejection reject;
};
template<
typename T,
typename Results
>
struct WhenAllRange
{
WhenAllRange(Resolver _resolve, Rejection _reject)
: resolve(std::move(_resolve))
, reject(std::move(_reject))
{ }
template<typename Iterator>
void operator()(Iterator first, Iterator last)
{
auto data = std::make_shared<DataT<T>>(
static_cast<size_t>(std::distance(first, last)),
std::move(resolve),
std::move(reject)
);
size_t index = 0;
for (auto it = first; it != last; ++it) {
WhenContinuation<T> cont(data, index);
it->then(std::move(cont), [=](std::exception_ptr ptr) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected) return;
data->rejected = true;
data->reject(std::move(ptr));
});
++index;
}
}
private:
struct Data
{
Data(size_t _total, Resolver _resolver, Rejection _rejection)
: total(_total)
, resolved(0)
, rejected(false)
, mtx()
, resolve(std::move(_resolver))
, reject(std::move(_rejection))
{ }
const size_t total;
size_t resolved;
bool rejected;
std::mutex mtx;
Resolver resolve;
Rejection reject;
};
/* Ok so apparently I can not fully specialize a template structure
* here, so you know what, compiler ? Take that Dummy type and leave
* me alone
*/
template<typename ValueType, typename Dummy = void> struct DataT : public Data {
DataT(size_t total, Resolver resolver, Rejection rejection)
: Data(total, std::move(resolver), std::move(rejection))
{
results.resize(total);
}
Results results;
};
/* For a vector of void promises, we do not have any results, that's
* why we need a distinct specialization for the void case
*/
template<typename Dummy> struct DataT<void, Dummy> : public Data {
DataT(size_t total, Resolver resolver, Rejection rejection)
: Data(total, std::move(resolver), std::move(rejection))
{
}
};
template<typename ValueType, typename Dummy = void>
struct WhenContinuation
{
using D = std::shared_ptr<DataT<ValueType>>;
WhenContinuation(const D& _data, size_t _index)
: data(_data)
, index(_index)
{ }
void operator()(const ValueType& val) const
{
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected) return;
data->results[index] = val;
data->resolved++;
if (data->resolved == data->total)
{
data->resolve(data->results);
}
}
D data;
size_t index;
};
template<typename Dummy>
struct WhenContinuation<void, Dummy>
{
using D = std::shared_ptr<DataT<void>>;
WhenContinuation(const D& _data, size_t)
: data(_data)
{ }
void operator()() const
{
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected) return;
data->resolved++;
if (data->resolved == data->total)
{
data->resolve();
}
}
D data;
};
Resolver resolve;
Rejection reject;
};
static constexpr Private::IgnoreException IgnoreException{};
static constexpr Private::NoExcept NoExcept{};
static constexpr Private::Throw Throw{};
namespace details {
/*
* Note that we could use std::result_of to SFINAE-out and dispatch to the right
* call However, gcc 4.7 does not correctly support std::result_of for SFINAE
* purposes, so we use a decltype SFINAE-expression instead.
*
* See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3462.html and
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56283 for reference
*/
template <typename T, typename Func>
auto callAsync(Func func, Resolver &resolver, Rejection &rejection)
-> decltype(std::declval<Func>()(resolver, rejection), void()) {
func(resolver, rejection);
}
template <typename T, typename Func>
auto callAsync(Func func, Resolver &resolver, Rejection &rejection)
-> decltype(std::declval<Func>()(Deferred<T>()), void()) {
func(Deferred<T>(std::move(resolver), std::move(rejection)));
}
} // namespace details
template <typename T> class Promise : public PromiseBase {
public:
template <typename U> friend class Promise;
typedef Private::CoreT<T> Core;
template <typename Func>
explicit Promise(Func func)
: core_(std::make_shared<Core>()), resolver_(core_), rejection_(core_) {
details::callAsync<T>(func, resolver_, rejection_);
}
Promise(const Promise<T> &other) = delete;
Promise &operator=(const Promise<T> &other) = delete;
Promise(Promise<T> &&other) = default;
Promise &operator=(Promise<T> &&other) = default;
virtual ~Promise() {}
template <typename U> static Promise<T> resolved(U &&value) {
static_assert(!std::is_void<T>::value,
"Can not resolve a void promise with parameters");
static_assert(std::is_same<T, U>::value || std::is_convertible<U, T>::value,
"Incompatible value type");
auto core = std::make_shared<Core>();
core->template construct<T>(std::forward<U>(value));
return Promise<T>(std::move(core));
}
static Promise<void> resolved() {
static_assert(std::is_void<T>::value,
"Resolving a non-void promise requires parameters");
auto core = std::make_shared<Core>();
core->state = State::Fulfilled;
return Promise<T>(std::move(core));
}
template <typename Exc> static Promise<T> rejected(Exc exc) {
auto core = std::make_shared<Core>();
core->exc = std::make_exception_ptr(exc);
core->state = State::Rejected;
return Promise<T>(std::move(core));
}
bool isPending() const override { return core_->state == State::Pending; }
bool isFulfilled() const override { return core_->state == State::Fulfilled; }
bool isRejected() const override { return core_->state == State::Rejected; }
template <typename ResolveFunc, typename RejectFunc>
auto then(ResolveFunc resolveFunc, RejectFunc rejectFunc)
-> Promise<typename detail::RemovePromise<
typename detail::FunctionTrait<ResolveFunc>::ReturnType>::Type> {
typedef typename detail::RemovePromise<
typename detail::FunctionTrait<ResolveFunc>::ReturnType>::Type RetType;
Promise<RetType> promise;
typedef Private::Continuation<T, ResolveFunc, RejectFunc, ResolveFunc>
Continuation;
std::shared_ptr<Private::Request> req =
std::make_shared<Continuation>(promise.core_, resolveFunc, rejectFunc);
std::unique_lock<std::mutex> guard(core_->mtx);
if (isFulfilled()) {
req->resolve(core_);
} else if (isRejected()) {
req->reject(core_);
} }
template< core_->requests.push_back(req);
typename... Args,
typename Results = return promise;
std::tuple< }
typename detail::RemovePromise<
typename std::remove_reference<Args>::type private:
>::Type... Promise()
> : core_(std::make_shared<Core>()), resolver_(core_), rejection_(core_) {}
>
Promise<Results> whenAll(Args&& ...args) { explicit Promise(std::shared_ptr<Core> &&core)
// As ugly as it looks, this is needed to bypass a bug of gcc < 4.9 : core_(core), resolver_(core_), rejection_(core_) {}
// whereby template parameters pack inside a lambda expression are not
// captured correctly and can not be expanded inside the lambda. std::shared_ptr<Core> core_;
Resolver* resolve; Resolver resolver_;
Rejection* reject; Rejection rejection_;
};
Promise<Results> promise([&](Resolver& resolver, Rejection& rejection) {
resolve = &resolver; template <typename T> class Barrier {
reject = &rejection; public:
explicit Barrier(Promise<T> &promise) : promise_(promise) {}
void wait() {
if (promise_.isFulfilled() || promise_.isRejected())
return;
promise_.then(
[&](const T &) mutable {
std::unique_lock<std::mutex> guard(mtx);
cv.notify_one();
},
[&](std::exception_ptr) mutable {
std::unique_lock<std::mutex> guard(mtx);
cv.notify_one();
});
std::unique_lock<std::mutex> guard(mtx);
cv.wait(guard,
[&] { return promise_.isFulfilled() || promise_.isRejected(); });
}
template <class Rep, class Period>
std::cv_status wait_for(const std::chrono::duration<Rep, Period> &period) {
if (promise_.isFulfilled() || promise_.isRejected())
return std::cv_status::no_timeout;
promise_.then(
[&](const T &) mutable {
std::unique_lock<std::mutex> guard(mtx);
cv.notify_one();
},
[&](std::exception_ptr) mutable {
std::unique_lock<std::mutex> guard(mtx);
cv.notify_one();
}); });
Impl::When<Impl::All> impl(std::move(*resolve), std::move(*reject)); std::unique_lock<std::mutex> guard(mtx);
// So we capture everything we need inside the lambda and then call the return cv.wait_for(guard, period);
// implementation and expand the parameters pack here }
impl(std::forward<Args>(args)...);
private:
Promise<T> &promise_;
mutable std::mutex mtx;
std::condition_variable cv;
};
namespace Impl {
struct Any;
}
class Any {
public:
friend struct Impl::Any;
Any(const Any &other) = default;
Any &operator=(const Any &other) = default;
Any(Any &&other) = default;
Any &operator=(Any &&other) = default;
template <typename T> bool is() const { return core_->id == TypeId::of<T>(); }
template <typename T> T cast() const {
if (!is<T>())
throw BadAnyCast();
auto core = std::static_pointer_cast<Private::CoreT<T>>(core_);
return core->value();
}
private:
explicit Any(const std::shared_ptr<Private::Core> &core) : core_(core) {}
std::shared_ptr<Private::Core> core_;
};
return promise; namespace Impl {
/* Instead of duplicating the code between whenAll and whenAny functions, the
* main implementation is in the When class below and we configure the class
* with a policy instead, depending if we are executing an "all" or "any"
* operation, how cool is that ?
*/
struct All {
struct Data {
Data(const size_t _total, Resolver _resolver, Rejection _rejection)
: total(_total), resolved(0), rejected(false), mtx(),
resolve(std::move(_resolver)), reject(std::move(_rejection)) {}
const size_t total;
size_t resolved;
bool rejected;
std::mutex mtx;
Resolver resolve;
Rejection reject;
};
template <size_t Index, typename T, typename Data>
static void resolveT(const T &val, Data &data) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected)
return;
// @Check thread-safety of std::get ?
std::get<Index>(data->results) = val;
data->resolved++;
if (data->resolved == data->total) {
data->resolve(data->results);
} }
}
template<typename... Args> template <typename Data> static void resolveVoid(Data &data) {
Promise<Any> whenAny(Args&& ...args) { std::lock_guard<std::mutex> guard(data->mtx);
// Same trick as above;
Resolver* resolve;
Rejection* reject;
Promise<Any> promise([&](Resolver& resolver, Rejection& rejection) { if (data->rejected)
resolve = &resolver; return;
reject = &rejection;
});
Impl::When<Impl::Any> impl(std::move(*resolve), std::move(*reject)); data->resolved++;
impl(std::forward<Args>(args)...);
return promise; if (data->resolved == data->total) {
data->resolve(data->results);
} }
}
template< template <typename Data>
typename Iterator, static void reject(std::exception_ptr exc, Data &data) {
typename ValueType std::lock_guard<std::mutex> guard(data->mtx);
= typename detail::RemovePromise<
typename std::iterator_traits<Iterator>::value_type
>::Type,
typename Results
= typename std::conditional<
std::is_same<void, ValueType>::value,
void, std::vector<ValueType>
>::type
>
Promise<Results> whenAll(Iterator first, Iterator last) {
/* @Security, assert that last >= first */
return Promise<Results>([=](Resolver& resolve, Rejection& rejection) { data->rejected = true;
data->reject(exc);
}
};
Impl::WhenAllRange<ValueType, Results> impl( struct Any {
std::move(resolve), std::move(rejection));
impl(first, last); struct Data {
Data(size_t, Resolver resolver, Rejection rejection)
: done(false), mtx(), resolve(std::move(resolver)),
reject(std::move(rejection)) {}
}); bool done;
std::mutex mtx;
Resolver resolve;
Rejection reject;
};
template <size_t Index, typename T, typename Data>
static void resolveT(const T &val, Data &data) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->done)
return;
// Instead of allocating a new core, ideally we could share the same core as
// the relevant promise but we do not have access to the promise here is so
// meh
auto core = std::make_shared<Private::CoreT<T>>();
core->template construct<T>(val);
data->resolve(Async::Any(core));
data->done = true;
}
template <typename Data> static void resolveVoid(Data &data) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->done)
return;
auto core = std::make_shared<Private::CoreT<void>>();
data->resolve(Async::Any(core));
data->done = true;
}
template <typename Data>
static void reject(std::exception_ptr exc, Data &data) {
std::lock_guard<std::mutex> guard(data->mtx);
data->done = true;
data->reject(exc);
}
};
template <typename ContinuationPolicy> struct When {
When(Resolver resolver, Rejection rejection)
: resolve(std::move(resolver)), reject(std::move(rejection)) {}
template <typename... Args> void operator()(Args &&... args) {
whenArgs(std::forward<Args>(args)...);
}
private:
template <typename T, size_t Index, typename Data> struct WhenContinuation {
explicit WhenContinuation(Data _data) : data(std::move(_data)) {}
void operator()(const T &val) const {
ContinuationPolicy::template resolveT<Index>(val, data);
}
Data data;
};
template <size_t Index, typename Data>
struct WhenContinuation<void, Index, Data> {
explicit WhenContinuation(Data _data) : data(std::move(_data)) {}
void operator()() const { ContinuationPolicy::resolveVoid(data); }
Data data;
};
template <typename T, size_t Index, typename Data>
WhenContinuation<T, Index, Data> makeContinuation(const Data &data) {
return WhenContinuation<T, Index, Data>(data);
}
template <size_t Index, typename Data, typename T>
void when(const Data &data, Promise<T> &promise) {
promise.then(makeContinuation<T, Index>(data), [=](std::exception_ptr ptr) {
ContinuationPolicy::reject(std::move(ptr), data);
});
}
template <size_t Index, typename Data, typename T>
void when(const Data &data, T &&arg) {
typedef typename std::remove_reference<T>::type CleanT;
auto promise = Promise<CleanT>::resolved(std::forward<T>(arg));
when<Index>(data, promise);
}
template <typename... Args> void whenArgs(Args &&... args) {
typedef std::tuple<typename detail::RemovePromise<
typename std::remove_reference<Args>::type>::Type...>
Results;
/* We need to keep the results alive until the last promise
* finishes its execution
*/
/* See the trick here ? Basically, we only have access to the real type of
* the results in this function. The policy classes do not have access to
* the full type (std::tuple), but, instead, take a generic template data
* type as a parameter. They only need to know that results is a tuple, they
* do not need to know the real type of the results.
*
* This is some sort of compile-time template type-erasing, hue
*/
struct Data : public ContinuationPolicy::Data {
Data(size_t total, Resolver resolver, Rejection rejection)
: ContinuationPolicy::Data(total, std::move(resolver),
std::move(rejection)) {}
Results results;
};
auto data = std::make_shared<Data>(sizeof...(Args), std::move(resolve),
std::move(reject));
whenArgs<0>(data, std::forward<Args>(args)...);
}
template <size_t Index, typename Data, typename Head, typename... Rest>
void whenArgs(const Data &data, Head &&head, Rest &&... rest) {
when<Index>(data, std::forward<Head>(head));
whenArgs<Index + 1>(data, std::forward<Rest>(rest)...);
}
template <size_t Index, typename Data, typename Head>
void whenArgs(const Data &data, Head &&head) {
when<Index>(data, std::forward<Head>(head));
}
Resolver resolve;
Rejection reject;
};
template <typename T, typename Results> struct WhenAllRange {
WhenAllRange(Resolver _resolve, Rejection _reject)
: resolve(std::move(_resolve)), reject(std::move(_reject)) {}
template <typename Iterator> void operator()(Iterator first, Iterator last) {
auto data = std::make_shared<DataT<T>>(
static_cast<size_t>(std::distance(first, last)), std::move(resolve),
std::move(reject));
size_t index = 0;
for (auto it = first; it != last; ++it) {
WhenContinuation<T> cont(data, index);
it->then(std::move(cont), [=](std::exception_ptr ptr) {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected)
return;
data->rejected = true;
data->reject(std::move(ptr));
});
++index;
}
}
private:
struct Data {
Data(size_t _total, Resolver _resolver, Rejection _rejection)
: total(_total), resolved(0), rejected(false), mtx(),
resolve(std::move(_resolver)), reject(std::move(_rejection)) {}
const size_t total;
size_t resolved;
bool rejected;
std::mutex mtx;
Resolver resolve;
Rejection reject;
};
/* Ok so apparently I can not fully specialize a template structure
* here, so you know what, compiler ? Take that Dummy type and leave
* me alone
*/
template <typename ValueType, typename Dummy = void>
struct DataT : public Data {
DataT(size_t total, Resolver resolver, Rejection rejection)
: Data(total, std::move(resolver), std::move(rejection)) {
results.resize(total);
}
Results results;
};
/* For a vector of void promises, we do not have any results, that's
* why we need a distinct specialization for the void case
*/
template <typename Dummy> struct DataT<void, Dummy> : public Data {
DataT(size_t total, Resolver resolver, Rejection rejection)
: Data(total, std::move(resolver), std::move(rejection)) {}
};
template <typename ValueType, typename Dummy = void> struct WhenContinuation {
using D = std::shared_ptr<DataT<ValueType>>;
WhenContinuation(const D &_data, size_t _index)
: data(_data), index(_index) {}
void operator()(const ValueType &val) const {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected)
return;
data->results[index] = val;
data->resolved++;
if (data->resolved == data->total) {
data->resolve(data->results);
}
}
D data;
size_t index;
};
template <typename Dummy> struct WhenContinuation<void, Dummy> {
using D = std::shared_ptr<DataT<void>>;
WhenContinuation(const D &_data, size_t) : data(_data) {}
void operator()() const {
std::lock_guard<std::mutex> guard(data->mtx);
if (data->rejected)
return;
data->resolved++;
if (data->resolved == data->total) {
data->resolve();
}
} }
D data;
};
Resolver resolve;
Rejection reject;
};
} // namespace Impl
template <typename... Args,
typename Results = std::tuple<typename detail::RemovePromise<
typename std::remove_reference<Args>::type>::Type...>>
Promise<Results> whenAll(Args &&... args) {
// As ugly as it looks, this is needed to bypass a bug of gcc < 4.9
// whereby template parameters pack inside a lambda expression are not
// captured correctly and can not be expanded inside the lambda.
Resolver *resolve;
Rejection *reject;
Promise<Results> promise([&](Resolver &resolver, Rejection &rejection) {
resolve = &resolver;
reject = &rejection;
});
Impl::When<Impl::All> impl(std::move(*resolve), std::move(*reject));
// So we capture everything we need inside the lambda and then call the
// implementation and expand the parameters pack here
impl(std::forward<Args>(args)...);
return promise;
}
template <typename... Args> Promise<Any> whenAny(Args &&... args) {
// Same trick as above;
Resolver *resolve;
Rejection *reject;
Promise<Any> promise([&](Resolver &resolver, Rejection &rejection) {
resolve = &resolver;
reject = &rejection;
});
Impl::When<Impl::Any> impl(std::move(*resolve), std::move(*reject));
impl(std::forward<Args>(args)...);
return promise;
}
template <typename Iterator,
typename ValueType = typename detail::RemovePromise<
typename std::iterator_traits<Iterator>::value_type>::Type,
typename Results =
typename std::conditional<std::is_same<void, ValueType>::value,
void, std::vector<ValueType>>::type>
Promise<Results> whenAll(Iterator first, Iterator last) {
/* @Security, assert that last >= first */
return Promise<Results>([=](Resolver &resolve, Rejection &rejection) {
Impl::WhenAllRange<ValueType, Results> impl(std::move(resolve),
std::move(rejection));
impl(first, last);
});
}
} // namespace Async } // namespace Async
} // namespace Pistache } // namespace Pistache
...@@ -255,7 +255,8 @@ public: ...@@ -255,7 +255,8 @@ public:
*/ */
explicit RouterHandler(std::shared_ptr<Rest::Router> router); explicit RouterHandler(std::shared_ptr<Rest::Router> router);
void onRequest(const Http::Request &req, Http::ResponseWriter response) override; void onRequest(const Http::Request &req,
Http::ResponseWriter response) override;
private: private:
std::shared_ptr<Tcp::Handler> clone() const final { std::shared_ptr<Tcp::Handler> clone() const final {
......
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