183 lines
4.1 KiB
C++
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__
|