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 @@
#include <pistache/typeid.h>
#include <type_traits>
#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <atomic>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <stdexcept>
#include <type_traits>
#include <typeinfo>
#include <vector>
namespace Pistache {
namespace Async {
class Error : public std::runtime_error {
public:
explicit Error(const char* what) : std::runtime_error(what) { }
explicit Error(const std::string& what) : std::runtime_error(what) { }
};
class Error : public std::runtime_error {
public:
explicit Error(const char *what) : std::runtime_error(what) {}
explicit Error(const std::string &what) : std::runtime_error(what) {}
};
class BadType : public Error {
public:
explicit BadType(TypeId id)
: Error("Argument type can not be used to resolve the promise "
" (TypeId does not match)")
, id_(std::move(id))
{ }
class BadType : public Error {
public:
explicit BadType(TypeId id)
: Error("Argument type can not be used to resolve the promise "
" (TypeId does not match)"),
id_(std::move(id)) {}
TypeId typeId() const {
return id_;
}
TypeId typeId() const { return id_; }
private:
TypeId id_;
};
private:
TypeId id_;
};
class BadAnyCast : public std::bad_cast {
public:
const char* what() const noexcept override { return "Bad any cast"; }
virtual ~BadAnyCast() { }
};
class BadAnyCast : public std::bad_cast {
public:
const char *what() const noexcept override { return "Bad any cast"; }
virtual ~BadAnyCast() {}
};
enum class State {
Pending, Fulfilled, Rejected
};
enum class State { Pending, Fulfilled, Rejected };
template<typename T> class Promise;
template <typename T> class Promise;
class PromiseBase {
public:
virtual ~PromiseBase() { }
virtual bool isPending() const = 0;
virtual bool isFulfilled() const = 0;
virtual bool isRejected() const = 0;
class PromiseBase {
public:
virtual ~PromiseBase() {}
virtual bool isPending() const = 0;
virtual bool isFulfilled() const = 0;
virtual bool isRejected() const = 0;
bool isSettled() const { return isFulfilled() || isRejected(); }
};
bool isSettled() const { return isFulfilled() || isRejected(); }
};
namespace detail {
template<typename Func, typename T>
struct IsCallable {
namespace detail {
template <typename Func, typename T> struct IsCallable {
template<typename U>
static auto test(U *) -> decltype(std::declval<Func>()(std::declval<U>()), std::true_type());
template <typename U>
static auto test(U *)
-> decltype(std::declval<Func>()(std::declval<U>()), std::true_type());
template<typename U>
static auto test(...) -> std::false_type;
template <typename U> 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>
struct IsMoveCallable : public IsMoveCallable<decltype(&Func::operator())> { };
template <typename Func>
struct IsMoveCallable : public IsMoveCallable<decltype(&Func::operator())> {};
template<typename R, typename Class, typename Arg>
struct IsMoveCallable<R (Class::*)(Arg) const> : public std::is_rvalue_reference<Arg> { };
template <typename R, typename Class, typename Arg>
struct IsMoveCallable<R (Class::*)(Arg) const>
: public std::is_rvalue_reference<Arg> {};
template<typename Func, typename Arg>
typename std::conditional<
IsMoveCallable<Func>::value,
Arg&&,
const Arg&
>::type tryMove(Arg& arg) {
return std::move(arg);
}
template <typename Func, typename Arg>
typename std::conditional<IsMoveCallable<Func>::value, Arg &&,
const 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>
struct FunctionTrait : public FunctionTrait<decltype(&Func::operator())> { };
template <size_t N, typename Head, typename... Tail>
struct nth_element<N, Head, Tail...> {
typedef typename nth_element<N - 1, Tail...>::type type;
};
template<typename R, typename Class, typename... Args>
struct FunctionTrait<R (Class::*)(Args...) const> {
typedef R ReturnType;
} // namespace detail
static constexpr size_t ArgsCount = sizeof...(Args);
};
namespace Private {
template<typename R, typename Class, typename... Args>
struct FunctionTrait<R (Class::*)(Args...)> {
typedef R ReturnType;
struct InternalRethrow {
explicit InternalRethrow(std::exception_ptr _exc) : exc(std::move(_exc)) {}
static constexpr size_t ArgsCount = sizeof...(Args);
};
std::exception_ptr exc;
};
template<typename T>
struct RemovePromise {
typedef T Type;
};
struct IgnoreException {
void operator()(std::exception_ptr) const {}
};
template<typename T>
struct RemovePromise<Promise<T>> {
typedef T Type;
};
struct NoExcept {
void operator()(std::exception_ptr) const { std::terminate(); }
};
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 nth_element<0, Head, Tail...> {
typedef Head type;
};
struct Core;
template<size_t N, typename Head, typename... Tail>
struct nth_element<N, Head, Tail...> {
typedef typename nth_element<N - 1, Tail...>::type type;
};
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);
}
namespace Private {
struct InternalRethrow {
explicit InternalRethrow(std::exception_ptr _exc)
: exc(std::move(_exc))
{ }
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))
{ }
};
void *mem = memory();
if (allocated) {
reinterpret_cast<T *>(mem)->~T();
allocated = false;
}
class Resolver {
public:
new (mem) T(std::forward<Args>(args)...);
allocated = true;
state = State::Fulfilled;
}
explicit Resolver(const std::shared_ptr<Private::Core> &core)
: core_(core)
{ }
virtual ~Core() {}
};
Resolver(const Resolver& other) = delete;
Resolver& operator=(const Resolver& other) = delete;
template <typename T> struct CoreT : public Core {
CoreT() : Core(State::Pending, TypeId::of<T>()), storage() {}
Resolver(Resolver&& other) = default;
Resolver& operator=(Resolver&& other) = default;
~CoreT() {
if (allocated) {
reinterpret_cast<T *>(&storage)->~T();
allocated = false;
}
}
template<typename Arg>
bool operator()(Arg&& arg) const {
if (!core_) return false;
template <class Other> struct Rebind { typedef CoreT<Other> Type; };
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)
throw Error("Attempt to resolve a fulfilled promise");
return *reinterpret_cast<T *>(&storage);
}
/* 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");
}
bool isVoid() const override { return false; }
std::unique_lock<std::mutex> guard(core_->mtx);
core_->construct<Type>(std::forward<Arg>(arg));
protected:
void *memory() override { return &storage; }
for (const auto& req: core_->requests) {
req->resolve(core_);
}
private:
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 {
if (!core_) return false;
bool isVoid() const override { return true; }
if (core_->state != State::Pending)
throw Error("Attempt to resolve a fulfilled promise");
protected:
void *memory() override { return nullptr; }
};
if (!core_->isVoid())
throw Error("Attempt ro resolve a non-void promise with no argument");
template <typename T> struct Continuable : public Request {
explicit Continuable(const std::shared_ptr<Core> &chain)
: resolveCount_(0), rejectCount_(0), chain_(chain) {}
std::unique_lock<std::mutex> guard(core_->mtx);
core_->state = State::Fulfilled;
for (const auto& req: core_->requests) {
req->resolve(core_);
}
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");
return true;
}
++resolveCount_;
doResolve(coreCast(core));
}
void clear() {
core_ = nullptr;
}
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");
Resolver clone() {
return Resolver(core_);
}
++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_);
}
}
}
private:
std::shared_ptr<Private::Core> core_;
};
std::shared_ptr<CoreT<T>> coreCast(const std::shared_ptr<Core> &core) const {
return std::static_pointer_cast<CoreT<T>>(core);
}
class Rejection {
public:
virtual void doResolve(const std::shared_ptr<CoreT<T>> &core) = 0;
virtual void doReject(const std::shared_ptr<CoreT<T>> &core) = 0;
explicit Rejection(const std::shared_ptr<Private::Core>& core)
: core_(core)
{ }
virtual ~Continuable() {}
Rejection(const Rejection& other) = delete;
Rejection& operator=(const Rejection& other) = delete;
size_t resolveCount_;
size_t rejectCount_;
std::shared_ptr<Core> chain_;
};
Rejection(Rejection&& other) = default;
Rejection& operator=(Rejection&& other) = default;
namespace impl {
template<typename Exc>
bool operator()(Exc exc) const {
if (!core_) return false;
template <typename T, typename Resolve, typename Reject, typename U>
struct Continuation;
if (core_->state != State::Pending)
throw Error("Attempt to reject a fulfilled promise");
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;
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_);
}
Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Reject reject)
: Base(chain, std::move(resolve), std::move(reject)) {}
};
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() {
core_ = nullptr;
}
Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Reject reject)
: Base(chain, std::move(resolve), std::move(reject)) {}
};
Rejection clone() {
return Rejection(core_);
}
// General specialization
template <typename T, typename Resolve, typename Reject, typename Res,
typename... Args>
struct Continuation<T, Resolve, Reject, Res(Args...)> : public Continuable<T> {
private:
std::shared_ptr<Private::Core> core_;
static_assert(sizeof...(Args) == 1,
"A continuation should only take one single argument");
};
typedef typename detail::nth_element<0, Args...>::type Arg;
template<typename T>
class Deferred {
public:
static_assert(std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
Deferred()
: resolver(nullptr)
, rejection(nullptr)
{ }
Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Reject reject)
: Continuable<T>(chain), resolve_(resolve), reject_(reject) {}
Deferred(const Deferred& other) = delete;
Deferred& operator=(const Deferred& other) = delete;
void doResolve(const std::shared_ptr<CoreT<T>> &core) override {
finishResolve(resolve_(detail::tryMove<Resolve>(core->value())));
}
Deferred(Deferred&& other) = default;
Deferred& operator=(Deferred&& other) = default;
void doReject(const std::shared_ptr<CoreT<T>> &core) override {
reject_(core->exc);
for (const auto &req : this->chain_->requests) {
req->reject(this->chain_);
}
}
Deferred(Resolver _resolver, Rejection _reject)
: resolver(std::move(_resolver))
, rejection(std::move(_reject))
{ }
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_);
}
}
template<typename U>
bool resolve(U&& arg) {
typedef typename std::remove_reference<U>::type CleanU;
Resolve resolve_;
Reject reject_;
};
static_assert(std::is_same<T, CleanU>::value || std::is_convertible<U, T>::value,
"Types mismatch");
// Specialization for a void-Promise
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>
void emplaceResolve(Args&& ...) {
}
static_assert(sizeof...(Args) == 0,
"Can not attach a non-void continuation to a void-Promise");
template<typename Exc>
bool reject(Exc exc) {
return rejection(std::move(exc));
}
void doResolve(const std::shared_ptr<CoreT<void>> & /*core*/) {
finishResolve(resolve_());
}
void clear() {
resolver.clear();
rejection.clear();
}
void doReject(const std::shared_ptr<CoreT<void>> &core) {
reject_(core->exc);
for (const auto &req : this->chain_->requests) {
req->reject(this->chain_);
}
}
private:
Resolver resolver;
Rejection rejection;
};
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_);
}
}
template<>
class Deferred<void> {
public:
Resolve resolve_;
Reject reject_;
};
Deferred()
: resolver(nullptr)
, rejection(nullptr)
{ }
// 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> {
Deferred(const Deferred& other) = delete;
Deferred& operator=(const Deferred& other) = delete;
Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Reject reject)
: Continuable<T>(chain), resolve_(resolve), reject_(reject) {}
Deferred(Deferred&& other) = default;
Deferred& operator=(Deferred&& other) = default;
static_assert(sizeof...(Args) == 1,
"A continuation should only take one single argument");
Deferred(Resolver _resolver, Rejection _reject)
: resolver(std::move(_resolver))
, rejection(std::move(_reject))
{ }
typedef typename detail::nth_element<0, Args...>::type Arg;
void resolve() {
resolver();
}
static_assert(std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
template<typename Exc>
void reject(Exc _exc) {
rejection(std::move(_exc));
}
void doResolve(const std::shared_ptr<CoreT<T>> &core) override {
resolve_(core->value());
}
private:
Resolver resolver;
Rejection rejection;
};
void doReject(const std::shared_ptr<CoreT<T>> &core) override {
reject_(core->exc);
}
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);
}
Resolve resolve_;
Reject reject_;
};
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)));
}
}
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_);
}
// 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> {
Promise(const Promise<T>& other) = delete;
Promise& operator=(const Promise<T>& other) = delete;
Continuation(const std::shared_ptr<Core> &chain, Resolve resolve,
Reject reject)
: Continuable<void>(chain), resolve_(resolve), reject_(reject) {}
Promise(Promise<T>&& other) = default;
Promise& operator=(Promise<T>&& other) = default;
static_assert(sizeof...(Args) == 0,
"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>
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));
}
Resolve resolve_;
Reject reject_;
};
static
Promise<void>
resolved() {
static_assert(std::is_void<T>::value,
"Resolving a non-void promise requires parameters");
// 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> {
auto core = std::make_shared<Core>();
core->state = State::Fulfilled;
return Promise<T>(std::move(core));
}
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);
}
}
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));
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);
}
}
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 PromiseType, typename Dummy = void> struct Chainer {
explicit Chainer(const std::shared_ptr<Private::Core> &core)
: chainCore(core) {}
template<typename ResolveFunc, typename RejectFunc>
auto
then(ResolveFunc resolveFunc, RejectFunc rejectFunc)
-> Promise<
typename detail::RemovePromise<
typename detail::FunctionTrait<ResolveFunc>::ReturnType
>::Type
>
{
void operator()(const PromiseType &val) {
chainCore->construct<PromiseType>(val);
for (const auto &req : chainCore->requests) {
req->resolve(chainCore);
}
}
typedef typename detail::RemovePromise<
typename detail::FunctionTrait<ResolveFunc>::ReturnType
>::Type RetType;
std::shared_ptr<Core> chainCore;
};
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;
std::shared_ptr<Private::Request> req = std::make_shared<Continuation>(promise.core_, resolveFunc, rejectFunc);
void operator()() {
auto core = this->chain_;
core->state = State::Fulfilled;
std::unique_lock<std::mutex> guard(core_->mtx);
if (isFulfilled()) {
req->resolve(core_);
}
else if (isRejected()) {
req->reject(core_);
}
for (const auto &req : chainCore->requests) {
req->resolve(chainCore);
}
}
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:
Promise()
: core_(std::make_shared<Core>())
, resolver_(core_)
, rejection_(core_)
{
}
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;
explicit Promise(std::shared_ptr<Core>&& core)
: core_(core)
, resolver_(core_)
, rejection_(core_)
{ }
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)) {}
};
} // 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_;
Resolver resolver_;
Rejection rejection_;
};
std::unique_lock<std::mutex> guard(core_->mtx);
core_->construct<Type>(std::forward<Arg>(arg));
template<typename T>
class Barrier {
public:
explicit Barrier(Promise<T>& promise)
: promise_(promise) {
}
for (const auto &req : core_->requests) {
req->resolve(core_);
}
void wait() {
if (promise_.isFulfilled() || promise_.isRejected()) return;
return true;
}
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();
});
bool operator()() const {
if (!core_)
return false;
std::unique_lock<std::mutex> guard(mtx);
cv.wait(guard, [&] { return promise_.isFulfilled() || promise_.isRejected(); });
}
if (core_->state != State::Pending)
throw Error("Attempt to resolve a fulfilled promise");
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;
if (!core_->isVoid())
throw Error("Attempt ro resolve a non-void promise with no argument");
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(core_->mtx);
core_->state = State::Fulfilled;
for (const auto &req : core_->requests) {
req->resolve(core_);
}
std::unique_lock<std::mutex> guard(mtx);
return cv.wait_for(guard, period);
}
return true;
}
private:
Promise<T>& promise_;
mutable std::mutex mtx;
std::condition_variable cv;
};
void clear() { core_ = nullptr; }
Resolver clone() { return Resolver(core_); }
private:
std::shared_ptr<Private::Core> core_;
};
namespace Impl {
struct Any;
class Rejection {
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 {
public:
friend struct Impl::Any;
return true;
}
Any(const Any& other) = default;
Any& operator=(const Any& other) = default;
void clear() { core_ = nullptr; }
Any(Any&& other) = default;
Any& operator=(Any&& other) = default;
Rejection clone() { return Rejection(core_); }
template<typename T>
bool is() const {
return core_->id == TypeId::of<T>();
}
private:
std::shared_ptr<Private::Core> core_;
};
template<typename T>
T cast() const {
if (!is<T>()) throw BadAnyCast();
template <typename T> class Deferred {
public:
Deferred() : resolver(nullptr), rejection(nullptr) {}
auto core = std::static_pointer_cast<Private::CoreT<T>>(core_);
return core->value();
}
Deferred(const Deferred &other) = delete;
Deferred &operator=(const Deferred &other) = delete;
private:
explicit Any(const std::shared_ptr<Private::Core>& core)
: core_(core)
{ }
std::shared_ptr<Private::Core> core_;
Deferred(Deferred &&other) = default;
Deferred &operator=(Deferred &&other) = default;
};
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
* 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 {
void resolve() { resolver(); }
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))
{ }
template <typename Exc> void reject(Exc _exc) { rejection(std::move(_exc)); }
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 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;
};
private:
Resolver resolver;
Rejection rejection;
};
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<
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;
core_->requests.push_back(req);
return promise;
}
private:
Promise()
: core_(std::make_shared<Core>()), resolver_(core_), rejection_(core_) {}
explicit Promise(std::shared_ptr<Core> &&core)
: core_(core), resolver_(core_), rejection_(core_) {}
std::shared_ptr<Core> core_;
Resolver resolver_;
Rejection rejection_;
};
template <typename T> class Barrier {
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));
// 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)...);
std::unique_lock<std::mutex> guard(mtx);
return cv.wait_for(guard, period);
}
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>
Promise<Any> whenAny(Args&& ...args) {
// Same trick as above;
Resolver* resolve;
Rejection* reject;
template <typename Data> static void resolveVoid(Data &data) {
std::lock_guard<std::mutex> guard(data->mtx);
Promise<Any> promise([&](Resolver& resolver, Rejection& rejection) {
resolve = &resolver;
reject = &rejection;
});
if (data->rejected)
return;
Impl::When<Impl::Any> impl(std::move(*resolve), std::move(*reject));
impl(std::forward<Args>(args)...);
return promise;
data->resolved++;
if (data->resolved == data->total) {
data->resolve(data->results);
}
}
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 */
template <typename Data>
static void reject(std::exception_ptr exc, Data &data) {
std::lock_guard<std::mutex> guard(data->mtx);
return Promise<Results>([=](Resolver& resolve, Rejection& rejection) {
data->rejected = true;
data->reject(exc);
}
};
Impl::WhenAllRange<ValueType, Results> impl(
std::move(resolve), std::move(rejection));
struct Any {
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 Pistache
......@@ -255,7 +255,8 @@ public:
*/
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:
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