Kylin/Universal/Private/PromiseData.h
2024-09-15 16:15:26 +08:00

183 lines
4.1 KiB
C++

#ifndef __PROMISEDATA_H__
#define __PROMISEDATA_H__
#include <cassert>
#include <list>
#include <memory>
#include <mutex>
template <typename T>
class PromiseValue {
public:
PromiseValue() {
}
PromiseValue(const T &data) : m_data(std::make_shared<T>(data)) {
}
PromiseValue(T &&data) : m_data(std::make_shared<T>(std::forward<T>(data))) {
}
bool isNull() const {
return m_data == nullptr;
}
const T &data() const {
return *m_data;
}
private:
std::shared_ptr<T> m_data;
};
class PromiseError {
public:
template <typename T>
PromiseError(const T &value) {
try {
throw value;
} catch (...) {
m_data = std::current_exception();
}
}
PromiseError() {
}
PromiseError(const std::exception_ptr &exception) : m_data{exception} {
}
void rethrow() const {
std::rethrow_exception(m_data);
}
bool isNull() const {
return m_data == nullptr;
}
private:
std::exception_ptr m_data; // std::exception_ptr is already a shared pointer
};
template <typename T, typename F>
class PromiseDataBase {
public:
using Handler = std::function<F>;
using Catcher = std::function<void(const PromiseError &)>;
bool isFulfilled() const {
return !isPending() && m_error.isNull();
}
bool isRejected() const {
return !isPending() && !m_error.isNull();
}
bool isPending() const {
std::lock_guard locker{m_mutex};
return !m_settled;
}
void addHandler(std::function<F> handler) {
std::lock_guard locker{m_mutex};
m_handlers.push_back(std::move(handler));
}
void addCatcher(std::function<void(const PromiseError &)> catcher) {
std::lock_guard locker{m_mutex};
m_catchers.push_back(std::move(catcher));
}
template <typename E>
void reject(E &&error) {
assert(isPending());
assert(m_error.isNull());
m_error = PromiseError{std::forward<E>(error)};
setSettled();
}
const PromiseError &error() const {
assert(isRejected());
return m_error;
}
void dispatch() {
if (isPending()) {
return;
}
m_mutex.lock();
auto handlers = std::move(m_handlers);
auto catchers = std::move(m_catchers);
m_mutex.unlock();
if (m_error.isNull()) {
notify(handlers);
return;
}
PromiseError error = m_error;
assert(!error.isNull());
for (auto &catcher : catchers) {
catcher(error);
}
}
protected:
virtual void notify(const std::list<Handler> &handlers) = 0;
void setSettled() {
std::lock_guard locker{m_mutex};
assert(!m_settled);
m_settled = true;
}
mutable std::mutex m_mutex;
private:
bool m_settled = false;
std::list<Handler> m_handlers;
std::list<Catcher> m_catchers;
PromiseError m_error;
};
template <typename T>
class PromiseData : public PromiseDataBase<T, void(const T &)> {
using Handler = typename PromiseDataBase<T, void(const T &)>::Handler;
public:
template <typename V>
void resolve(V &&value) {
assert(this->isPending());
assert(m_value.isNull());
m_value = PromiseValue<T>{std::forward<V>(value)};
this->setSettled();
}
const PromiseValue<T> &value() const {
assert(this->isFulfilled());
return m_value;
}
protected:
void notify(const std::list<Handler> &handlers) final {
PromiseValue<T> value = m_value;
assert(!value.isNull());
for (auto &handler : handlers) {
handler(value.data());
}
}
private:
PromiseValue<T> m_value;
};
template <>
class PromiseData<void> : public PromiseDataBase<void, void()> {
using Handler = PromiseDataBase<void, void()>::Handler;
public:
void resolve() {
setSettled();
}
protected:
void notify(const std::list<Handler> &handlers) {
for (const auto &handler : handlers) {
handler();
}
}
};
#endif // __PROMISEDATA_H__