Commit 254e46f5 authored by octal's avatar octal

async: refactored Promise continuation implementation, allowing more types to...

async: refactored Promise continuation implementation, allowing more types to be passed-in as a continuation
parent 61f42550
...@@ -47,19 +47,6 @@ namespace Async { ...@@ -47,19 +47,6 @@ namespace Async {
virtual const char* what() const noexcept { return "Bad any cast"; } virtual const char* what() const noexcept { return "Bad any cast"; }
}; };
/*
- Direct: The continuation will be directly called in the context
of the same thread
- Deferred: The continuation will be called in an asynchronous way
Note that Deferred requires the Promise to be bound to an event
loop
*/
enum class Continuation {
Direct,
Deferred
};
enum class State { enum class State {
Pending, Fulfilled, Rejected Pending, Fulfilled, Rejected
}; };
...@@ -131,6 +118,18 @@ namespace Async { ...@@ -131,6 +118,18 @@ namespace Async {
typedef T Type; 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<size_t N, typename Head, typename... Tail>
struct nth_element<N, Head, Tail...> {
typedef typename nth_element<N - 1, Tail...>::type type;
};
} }
namespace Private { namespace Private {
...@@ -295,236 +294,419 @@ namespace Async { ...@@ -295,236 +294,419 @@ namespace Async {
std::shared_ptr<Core> chain_; std::shared_ptr<Core> chain_;
}; };
template<typename T, typename ResolveFunc, typename RejectFunc> namespace impl {
struct ThenContinuation : public Continuable<T> {
ThenContinuation(
const std::shared_ptr<Core>& chain,
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc)
: Continuable<T>(chain)
, resolveFunc_(std::forward<ResolveFunc>(resolveFunc))
, rejectFunc_(std::forward<RejectFunc>(rejectFunc))
{
}
void doResolve(const std::shared_ptr<CoreT<T>>& core) { template<typename T, typename Resolve, typename Reject, typename U> struct Continuation;
doResolveImpl(core, std::is_void<T>());
}
void doReject(const std::shared_ptr<CoreT<T>>& core) { template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
rejectFunc_(core->exc); struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const> : public Continuation<T, Resolve, Reject, Res (Args...)> {
} typedef Continuation<T, Resolve, Reject, Res (Args...)> Base;
void doResolveImpl(const std::shared_ptr<CoreT<T>>& core, std::true_type /* is_void */) { Continuation(
resolveFunc_(); const std::shared_ptr<Core>& chain,
} Resolve resolve,
Reject reject)
: Base(chain, std::move(resolve), std::move(reject))
{ }
};
void doResolveImpl(const std::shared_ptr<CoreT<T>>& core, std::false_type /* is_void */) { template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
resolveFunc_(core->value()); struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)> : public Continuation<T, Resolve, Reject, Res (Args...)> {
} typedef Continuation<T, Resolve, Reject, Res (Args...)> Base;
ResolveFunc resolveFunc_; Continuation(
RejectFunc rejectFunc_; const std::shared_ptr<Core>& chain,
}; Resolve resolve,
Reject reject)
: Base(chain, std::move(resolve), std::move(reject))
{ }
};
template<typename T, typename ResolveFunc, typename RejectFunc, typename Return> // General specialization
struct ThenReturnContinuation : public Continuable<T> { template<typename T, typename Resolve, typename Reject, typename Res, typename... Args>
ThenReturnContinuation( struct Continuation<T, Resolve, Reject, Res (Args...)> : public Continuable<T> {
const std::shared_ptr<Core>& chain,
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc)
: Continuable<T>(chain)
, resolveFunc_(std::forward<ResolveFunc>(resolveFunc))
, rejectFunc_(std::forward<RejectFunc>(rejectFunc))
{
}
void doResolve(const std::shared_ptr<CoreT<T>>& core) { static_assert(sizeof...(Args) == 1,
doResolveImpl(core, std::is_void<T>()); "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 doReject(const std::shared_ptr<CoreT<T>>& core) { void doResolve(const std::shared_ptr<CoreT<T>>& core) {
rejectFunc_(core->exc); finishResolve(resolve_(detail::tryMove<Resolve>(core->value())));
for (const auto& req: this->chain_->requests) {
req->reject(this->chain_);
} }
}
void doResolveImpl(const std::shared_ptr<CoreT<T>>& core, std::true_type /* is_void */) { void doReject(const std::shared_ptr<CoreT<T>>& core) {
finishResolve(resolveFunc_()); reject_(core->exc);
} for (const auto& req: this->chain_->requests) {
req->reject(this->chain_);
}
}
void doResolveImpl(const std::shared_ptr<CoreT<T>>& core, std::false_type /* is_void */) { template<typename Ret>
finishResolve(resolveFunc_(detail::tryMove<ResolveFunc>(core->value()))); 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_;
};
template<typename Ret> // Specialization for a void-Promise
void finishResolve(Ret&& ret) const { template<typename Resolve, typename Reject, typename Res, typename... Args>
typedef typename std::remove_reference<Ret>::type CleanRet; struct Continuation<void, Resolve, Reject, Res (Args...)> : public Continuable<void> {
this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
for (const auto& req: this->chain_->requests) { Continuation(
req->resolve(this->chain_); 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_());
} }
}
ResolveFunc resolveFunc_; void doReject(const std::shared_ptr<CoreT<void>>& core) {
RejectFunc rejectFunc_; reject_(core->exc);
}; for (const auto& req: this->chain_->requests) {
req->reject(this->chain_);
}
}
template<typename T, typename ResolveFunc, typename RejectFunc> template<typename Ret>
struct ThenChainContinuation : public Continuable<T> { void finishResolve(Ret&& ret) const {
ThenChainContinuation( typedef typename std::remove_reference<Ret>::type CleanRet;
const std::shared_ptr<Core>& chain, this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) for (const auto& req: this->chain_->requests) {
: Continuable<T>(chain) req->resolve(this->chain_);
, resolveFunc_(std::forward<ResolveFunc>(resolveFunc)) }
, rejectFunc_(std::forward<RejectFunc>(rejectFunc)) }
{
} 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) {
doResolveImpl(core, std::is_void<T>());
}
void doReject(const std::shared_ptr<CoreT<T>>& core) { void doResolve(const std::shared_ptr<CoreT<T>>& core) {
rejectFunc_(core->exc); resolve_(core->value());
for (const auto& req: core->requests) {
req->reject(core);
} }
}
void doResolveImpl(const std::shared_ptr<CoreT<T>>& core, std::true_type /* is_void */) { void doReject(const std::shared_ptr<CoreT<T>>& core) {
auto promise = resolveFunc_(); reject_(core->exc);
finishResolve(promise); }
}
void doResolveImpl(const std::shared_ptr<CoreT<T>>& core, std::false_type /* is_void */) { Resolve resolve_;
auto promise = resolveFunc_(detail::tryMove<ResolveFunc>(core->value())); Reject reject_;
finishResolve(promise); };
}
// 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) {
resolve_();
}
void doReject(const std::shared_ptr<CoreT<void>>& core) {
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");
template<typename P> Continuation(
void finishResolve(P& promise) { const std::shared_ptr<Core>& chain,
auto chainer = makeChainer(promise); Resolve resolve,
promise.then(std::move(chainer), [=](std::exception_ptr exc) { Reject reject)
auto core = this->chain_; : Continuable<T>(chain)
core->exc = std::move(exc); , resolve_(resolve)
core->state = State::Rejected; , reject_(reject)
{ }
void doResolve(const std::shared_ptr<CoreT<T>>& core) {
auto promise = resolve_(detail::tryMove<Resolve>(core->value()));
finishResolve(promise);
}
void doReject(const std::shared_ptr<CoreT<T>>& core) {
reject_(core->exc);
for (const auto& req: core->requests) { for (const auto& req: core->requests) {
req->reject(core); req->reject(core);
} }
}); }
}
template<typename PromiseType>
struct Chainer {
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);
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_;
ResolveFunc resolveFunc_; };
RejectFunc rejectFunc_;
// 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");
template<typename PromiseType> Continuation(
struct Chainer { const std::shared_ptr<Core>& chain,
Chainer(const std::shared_ptr<Private::Core>& core) Resolve resolve,
: chainCore(core) Reject reject)
: Continuable<void>(chain)
, resolve_(resolve)
, reject_(reject)
{ } { }
void operator()(const PromiseType& val) { void doResolve(const std::shared_ptr<CoreT<void>>& core) {
chainCore->construct<PromiseType>(val); auto promise = resolve_();
for (const auto& req: chainCore->requests) { finishResolve(promise);
req->resolve(chainCore); }
void doReject(const std::shared_ptr<CoreT<void>>& core) {
reject_(core->exc);
for (const auto& req: core->requests) {
req->reject(core);
} }
} }
std::shared_ptr<Core> chainCore; template<typename PromiseType, typename Dummy = void>
struct Chainer {
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> {
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_;
}; };
template< } // namespace impl
typename Promise,
typename Type = typename detail::RemovePromise<Promise>::Type>
Chainer<Type>
makeChainer(const Promise&) {
return Chainer<Type>(this->chain_);
}
}; template<typename T, typename Resolve, typename Reject, typename Sig>
struct Continuation : public impl::Continuation<T, Resolve, Reject, decltype(&Sig::operator())>
{
template<typename T, typename ResolveFunc> typedef impl::Continuation<T, Resolve, Reject, decltype(&Sig::operator())> Base;
struct ContinuationFactory : public ContinuationFactory<T, decltype(&ResolveFunc::operator())> { };
/* Const */
template<typename T, typename R, typename Class, typename... Args>
struct ContinuationFactory<T, R (Class::*)(Args...) const> {
template<typename ResolveFunc, typename RejectFunc>
static Continuable<T>* create(
const std::shared_ptr<Core>& chain,
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) {
return new ThenReturnContinuation<T, ResolveFunc, RejectFunc, R>(
chain,
std::forward<ResolveFunc>(resolveFunc),
std::forward<RejectFunc>(rejectFunc));
}
};
template<typename T, typename Class, typename... Args> Continuation(
struct ContinuationFactory<T, void (Class::*)(Args ...) const> { const std::shared_ptr<Core>& core,
template<typename ResolveFunc, typename RejectFunc> Resolve resolve,
static Continuable<T>* create( Reject reject)
const std::shared_ptr<Private::Core>& chain, : Base(core, std::move(resolve), std::move(reject))
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) { { }
return new ThenContinuation<T, ResolveFunc, RejectFunc>(
chain,
std::forward<ResolveFunc>(resolveFunc),
std::forward<RejectFunc>(rejectFunc));
}
}; };
template<typename T, typename U, typename Class, typename... Args> template<typename T, typename Resolve, typename Reject, typename Res, typename... Args>
struct ContinuationFactory<T, Promise<U> (Class::*)(Args...) const> { struct Continuation<T, Resolve, Reject, Res (*)(Args...)> : public impl::Continuation<T, Resolve, Reject, Res (Args...)>
template<typename ResolveFunc, typename RejectFunc> {
static Continuable<T>* create( typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
const std::shared_ptr<Private::Core>& chain,
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) { Continuation(
return new ThenChainContinuation<T, ResolveFunc, RejectFunc>( const std::shared_ptr<Core>& core,
chain, Resolve resolve,
std::forward<ResolveFunc>(resolveFunc), Reject reject)
std::forward<RejectFunc>(rejectFunc)); : Base(core, std::move(resolve), std::move(reject))
} { }
}; };
/* Non-const */ template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
template<typename T, typename R, typename Class, typename... Args> struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)> : public impl::Continuation<T, Resolve, Reject, Res (Args...)>
struct ContinuationFactory<T, R (Class::*)(Args...)> { {
template<typename ResolveFunc, typename RejectFunc> typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
static Continuable<T>* create(
const std::shared_ptr<Core>& chain, Continuation(
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) { const std::shared_ptr<Core>& core,
return new ThenReturnContinuation<T, ResolveFunc, RejectFunc, R>( Resolve resolve,
chain, Reject reject)
std::forward<ResolveFunc>(resolveFunc), : Base(core, std::move(resolve), std::move(reject))
std::forward<RejectFunc>(rejectFunc)); { }
}
}; };
template<typename T, typename Class, typename... Args> template<typename T, typename Resolve, typename Reject, typename Res, typename Cls, typename... Args>
struct ContinuationFactory<T, void (Class::*)(Args ...)> { struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const> : public impl::Continuation<T, Resolve, Reject, Res (Args...)>
template<typename ResolveFunc, typename RejectFunc> {
static Continuable<T>* create( typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
const std::shared_ptr<Private::Core>& chain,
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) { Continuation(
return new ThenContinuation<T, ResolveFunc, RejectFunc>( const std::shared_ptr<Core>& core,
chain, Resolve resolve,
std::forward<ResolveFunc>(resolveFunc), Reject reject)
std::forward<RejectFunc>(rejectFunc)); : Base(core, std::move(resolve), std::move(reject))
} { }
}; };
template<typename T, typename U, typename Class, typename... Args> template<typename T, typename Resolve, typename Reject, typename Res, typename... Args>
struct ContinuationFactory<T, Promise<U> (Class::*)(Args...)> { struct Continuation<T, Resolve, Reject, std::function<Res (Args...)>> : public impl::Continuation<T, Resolve, Reject, Res (Args...)> {
template<typename ResolveFunc, typename RejectFunc> typedef impl::Continuation<T, Resolve, Reject, Res (Args...)> Base;
static Continuable<T>* create(
const std::shared_ptr<Private::Core>& chain, Continuation(
ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc) { const std::shared_ptr<Core>& core,
return new ThenChainContinuation<T, ResolveFunc, RejectFunc>( Resolve resolve,
chain, Reject reject)
std::forward<ResolveFunc>(resolveFunc), : Base(core, std::move(resolve), std::move(reject))
std::forward<RejectFunc>(rejectFunc)); { }
}
}; };
} }
...@@ -818,7 +1000,7 @@ namespace Async { ...@@ -818,7 +1000,7 @@ namespace Async {
template<typename ResolveFunc, typename RejectFunc> template<typename ResolveFunc, typename RejectFunc>
auto auto
then(ResolveFunc&& resolveFunc, RejectFunc&& rejectFunc, Continuation type = Continuation::Direct) then(ResolveFunc resolveFunc, RejectFunc rejectFunc)
-> Promise< -> Promise<
typename detail::RemovePromise< typename detail::RemovePromise<
typename detail::FunctionTrait<ResolveFunc>::ReturnType typename detail::FunctionTrait<ResolveFunc>::ReturnType
...@@ -826,23 +1008,14 @@ namespace Async { ...@@ -826,23 +1008,14 @@ namespace Async {
> >
{ {
thenStaticChecks<ResolveFunc>(std::is_void<T>());
typedef typename detail::RemovePromise< typedef typename detail::RemovePromise<
typename detail::FunctionTrait<ResolveFunc>::ReturnType typename detail::FunctionTrait<ResolveFunc>::ReturnType
>::Type RetType; >::Type RetType;
Promise<RetType> promise; Promise<RetType> promise;
// Due to how template argument deduction works on universal references, we need to remove any reference from typedef Private::Continuation<T, ResolveFunc, RejectFunc, ResolveFunc> Continuation;
// the deduced function type, fun fun fun std::shared_ptr<Private::Request> req = std::make_shared<Continuation>(promise.core_, resolveFunc, rejectFunc);
typedef typename std::remove_reference<ResolveFunc>::type ResolveFuncType;
typedef typename std::remove_reference<RejectFunc>::type RejectFuncType;
std::shared_ptr<Private::Request> req;
req.reset(Private::ContinuationFactory<T, ResolveFuncType>::create(
promise.core_,
std::forward<ResolveFunc>(resolveFunc),
std::forward<RejectFunc>(rejectFunc)));
std::unique_lock<std::mutex> guard(core_->mtx); std::unique_lock<std::mutex> guard(core_->mtx);
if (isFulfilled()) { if (isFulfilled()) {
...@@ -871,18 +1044,6 @@ namespace Async { ...@@ -871,18 +1044,6 @@ namespace Async {
, rejection_(core_) , rejection_(core_)
{ } { }
template<typename ResolveFunc>
void thenStaticChecks(std::true_type /* is_void */) {
static_assert(detail::FunctionTrait<ResolveFunc>::ArgsCount == 0,
"Continuation function of a void promise should not take any argument");
}
template<typename ResolveFunc>
void thenStaticChecks(std::false_type /* is_void */) {
static_assert(detail::IsCallable<ResolveFunc, T>::value,
"Function is not compatible with underlying promise type");
}
std::shared_ptr<Core> core_; std::shared_ptr<Core> core_;
Resolver resolver_; Resolver resolver_;
Rejection rejection_; Rejection rejection_;
......
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