Kylin/Universal/Promise.h
2024-09-15 23:39:23 +08:00

327 lines
10 KiB
C++

#ifndef __PROMISE_H__
#define __PROMISE_H__
#include <exception>
class PromiseUndefinedException : public std::exception {
public:
};
class PromiseConversionException : public std::exception {
public:
};
#include "Private/PromiseHandler.h"
#include "Private/PromiseResolver.h"
#include <thread>
template <typename T>
class Promise;
template <typename T>
class PromiseBase {
friend struct PromiseFulfill<Promise<T>>;
public:
using Type = T;
template <typename F, typename std::enable_if<ArgsOf<F>::count == 1, int>::type = 0>
PromiseBase(F callback) : m_d{std::make_shared<PromiseData<T>>()} {
PromiseResolver<T> resolver{*this};
try {
callback(PromiseResolve<T>(resolver));
} catch (...) {
resolver.reject(std::current_exception());
}
}
template <typename F, typename std::enable_if<ArgsOf<F>::count != 1, int>::type = 0>
PromiseBase(F callback) : m_d{std::make_shared<PromiseData<T>>()} {
PromiseResolver<T> resolver{*this};
try {
callback(PromiseResolve<T>(resolver), PromiseReject<T>(resolver));
} catch (...) {
resolver.reject(std::current_exception());
}
}
PromiseBase(const PromiseBase<T> &other) : m_d{other.m_d} {
}
PromiseBase(const Promise<T> &other) : m_d{other.m_d} {
}
PromiseBase(PromiseBase<T> &&other) {
swap(other);
}
virtual ~PromiseBase() {
}
PromiseBase<T> &operator=(const PromiseBase<T> &other) {
m_d = other.m_d;
return *this;
}
PromiseBase<T> &operator=(PromiseBase<T> &&other) {
PromiseBase<T>(std::move(other)).swap(*this);
return *this;
}
bool operator==(const PromiseBase<T> &other) const {
return (m_d == other.m_d);
}
bool operator!=(const PromiseBase<T> &other) const {
return (m_d != other.m_d);
}
void swap(PromiseBase<T> &other) {
std::swap(m_d, other.m_d);
}
bool isFulfilled() const {
return m_d->isFulfilled();
}
bool isRejected() const {
return m_d->isRejected();
}
bool isPending() const {
return m_d->isPending();
}
template <typename TFulfilled, typename TRejected>
typename PromiseHandler<T, TFulfilled>::PromiseType then(const TFulfilled &fulfilled, const TRejected &rejected) const {
using PromiseType = typename PromiseHandler<T, TFulfilled>::PromiseType;
PromiseType next(
[&](const PromiseResolve<typename PromiseType::Type> &resolve, const PromiseReject<typename PromiseType::Type> &reject) {
m_d->addHandler(PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject));
m_d->addCatcher(PromiseCatcher<T, TRejected>::create(rejected, resolve, reject));
});
if (!m_d->isPending()) {
m_d->dispatch();
}
return next;
}
template <typename TFulfilled>
typename PromiseHandler<T, TFulfilled>::PromiseType then(TFulfilled &&fulfilled) const {
return then(std::forward<TFulfilled>(fulfilled), nullptr);
}
template <typename THandler>
Promise<T> finally(THandler handler) const {
Promise<T> p = *this;
return p.then(handler, handler).then([=]() { return p; });
}
template <typename TRejected>
typename PromiseHandler<T, std::nullptr_t>::PromiseType fail(TRejected &&rejected) const {
return then(nullptr, std::forward<TRejected>(rejected));
}
Promise<T> wait() const {
while (m_d->isPending()) {
std::this_thread::yield();
}
return *this;
}
template <typename E>
static Promise<T> reject(E &&error) {
return Promise<T>{[&](const PromiseResolve<T> &, const PromiseReject<T> &reject) { reject(std::forward<E>(error)); }};
}
template <typename THandler>
Promise<T> tapFail(THandler handler) const {
Promise<T> p = *this;
return p.then([]() {}, handler).then([=]() { return p; });
}
template <typename THandler>
Promise<T> tap(THandler handler) const {
Promise<T> p = *this;
return p.then(handler).then([=]() { return p; });
}
#ifdef BOOST_ASIO_EXECUTION_EXECUTOR
template <class Executor, class Rep, class Period>
Promise<T> delay(Executor &ioContext, const std::chrono::duration<Rep, Period> &duration) const {
return tap([&]() {
return Promise<void>{[&](const PromiseResolve<void> &resolve) {
auto timer = std::make_shared<boost::asio::steady_timer>(ioContext, duration);
timer->async_wait([timer, resolve](const boost::system::error_code & /*e*/) mutable {
boost::asio::post(timer->get_executor(), resolve);
});
}};
});
}
template <class Executor>
Promise<T> delay(Executor &ioContext, int milliseconds) const {
return delay(ioContext, std::chrono::milliseconds(milliseconds));
}
#endif
protected:
std::shared_ptr<PromiseData<T>> m_d;
};
namespace PromiseHelper {
template <typename T>
typename PromiseDeduce<T>::Type resolve(T &&value) {
using PromiseType = typename PromiseDeduce<T>::Type;
using ValueType = typename PromiseType::Type;
using ResolveType = PromiseResolve<ValueType>;
using RejectType = PromiseReject<ValueType>;
return PromiseType{[&](ResolveType &&resolve, RejectType &&reject) {
PromiseFulfill<std::decay_t<T>>::call(std::forward<T>(value), std::forward<ResolveType>(resolve), std::forward<RejectType>(reject));
}};
}
Promise<void> resolve();
template <typename Functor, typename... Args>
typename PromiseFunctor<Functor, Args...>::PromiseType attempt(Functor &&fn, Args &&...args) {
using FunctorType = PromiseFunctor<Functor, Args...>;
using PromiseType = typename FunctorType::PromiseType;
using ValueType = typename PromiseType::Type;
// NOTE: std::forward<T<U>>: MSVC 2013 fails when forwarding
// template type (error: "expects 4 arguments - 0 provided").
// However it succeeds with type alias.
// TODO: should we expose QPromise::ResolveType & RejectType?
using ResolveType = PromiseResolve<ValueType>;
using RejectType = PromiseReject<ValueType>;
return PromiseType{[&](ResolveType &&resolve, RejectType &&reject) {
PromiseDispatch<typename FunctorType::ResultType>::call(std::forward<ResolveType>(resolve), std::forward<RejectType>(reject),
std::forward<Functor>(fn), std::forward<Args>(args)...);
}};
}
template <typename T, template <typename, typename...> class Sequence = std::vector, typename... Args>
Promise<std::vector<T>> all(const Sequence<Promise<T>, Args...> &promises) {
const int count = static_cast<int>(promises.size());
if (count == 0) {
return PromiseHelper::resolve(std::vector<T>{});
}
return Promise<std::vector<T>>{[=](const PromiseResolve<std::vector<T>> &resolve, const PromiseReject<std::vector<T>> &reject) {
auto remaining = std::make_shared<int>(count);
auto results = std::make_shared<std::vector<T>>(count);
int i = 0;
for (const auto &promise : promises) {
promise.then(
[=](const T &res) mutable {
(*results)[i] = res;
if (--(*remaining) == 0) {
resolve(*results);
}
},
[=]() mutable {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
i++;
}
}};
}
template <template <typename, typename...> class Sequence = std::vector, typename... Args>
Promise<void> all(const Sequence<Promise<void>, Args...> &promises);
}; // namespace PromiseHelper
template <typename T>
class Promise : public PromiseBase<T> {
friend class PromiseResolver<T>;
public:
template <typename F>
Promise(F &&resolver) : PromiseBase<T>(std::forward<F>(resolver)) {
}
template <typename U>
Promise<U> convert() const {
return PromiseBase<T>::then(PromiseConverter<T, U>::create());
}
template <typename Functor>
Promise<T> each(Functor fn) {
return this->tap([=](const T &values) {
int i = 0;
std::vector<Promise<void>> promises;
for (const auto &v : values) {
promises.push_back(PromiseHelper::attempt(fn, v, i).then([]() {
// Cast to void in case fn returns a non promise value.
// TODO remove when implicit cast is implemented.
}));
i++;
}
return PromiseHelper::all(promises);
});
}
static Promise<T> resolve(const T &value) {
return Promise<T>{[&](const PromiseResolve<T> &resolve) { resolve(value); }};
}
};
template <>
class Promise<void> : public PromiseBase<void> {
friend class PromiseResolver<void>;
public:
template <typename F>
Promise(F &&resolver) : PromiseBase<void>(std::forward<F>(resolver)) {
}
public:
static Promise<void> resolve() {
return Promise<void>{[](const PromiseResolve<void> &resolve) { resolve(); }};
}
private:
friend class PromiseBase<void>;
};
namespace PromiseHelper {
Promise<void> resolve() {
return Promise<void>{[](const PromiseResolve<void> &resolve) { resolve(); }};
}
template <template <typename, typename...> class Sequence = std::vector, typename... Args>
Promise<void> all(const Sequence<Promise<void>, Args...> &promises) {
const int count = static_cast<int>(promises.size());
if (count == 0) {
return PromiseHelper::resolve();
}
return Promise<void>{[=](const PromiseResolve<void> &resolve, const PromiseReject<void> &reject) {
auto remaining = std::make_shared<int>(count);
for (const auto &promise : promises) {
promise.then(
[=]() {
if (--(*remaining) == 0) {
resolve();
}
},
[=]() {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
}
}};
}
} // namespace PromiseHelper
#endif // __PROMISE_H__