Skip to content

Instantly share code, notes, and snippets.

@RedBeard0531
Created September 21, 2018 15:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RedBeard0531/0747a6f4fb678dee8753fae7be1b5257 to your computer and use it in GitHub Desktop.
Save RedBeard0531/0747a6f4fb678dee8753fae7be1b5257 to your computer and use it in GitHub Desktop.
#include <type_traits>
#include <future>
#include <functional>
template <typename T>
struct expected {
expected(const T&) {}
};
template <typename T>
struct Promise {
void set_with(std::function<T()>);
};
template <typename T>
struct LazyFuture;
template <typename T>
struct [[nodiscard]] Future {
using value_type = T;
static constexpr bool isLazy = false;
static auto make_promise_contract() {
struct PromiseContract {
Future<T> fut;
Promise<T> promise;
};
return PromiseContract();
}
LazyFuture<T> lazify();
Future<T> eagerize() { return *this; }
expected<T> get_expected();
void finally(std::function<void(expected<T>)>);
};
template <typename T>
using Receiver = std::function<void(expected<T>)>;
template <typename T>
using Sender = std::function<void(Receiver<T>)>;
template <typename T>
struct [[nodiscard]] LazyFuture {
static constexpr bool isLazy = true;
using value_type = T;
LazyFuture<T> lazify() { return *this; }
Future<T> eagerize();
void finally(std::function<void(expected<T>)> func) {
submit(func);
}
expected<T> get_expected() {
return eagerize().get_expected();
}
Sender<T> submit;
};
template <typename T>
Future<T> LazyFuture<T>::eagerize() {
auto [fut, p] = Future<T>::make_promise_contract();
submit([p = p] (expected<T>) mutable noexcept {
p.set_with(std::make_from_tuple<T>());
});
return fut;
}
template <typename T>
LazyFuture<T> Future<T>::lazify() {
#if 0 // old impl
struct State {
std::atomic<int> count{0};
std::optional<expected<T>> val;
std::optional<Receiver<T>> receiver;
};
auto state = std::make_shared<State>();
finally([state] (expected<T> val) mutable noexcept {
state->val.emplace(val);
if (state->count++ == 1)
return;
(state->receiver)(*state->val);
});
return LazyFuture<T>{[state] (Receiver<T> receiver) {
state->receiver.emplace(receiver);
if (state->count++ == 1)
return;
(state->receiver)(*state->val);
}};
#else
return { [self = std::move(*this)] (Receiver<T> receiver) mutable noexcept {
self.finally(receiver);
}};
#endif
}
template <typename T>
class SharedFuture {
public:
template<typename AnyFutureT>
SharedFuture(AnyFutureT future) {
future.finally([self = *this] (expected<T> result) mutable noexcept {
{
auto lk = std::unique_lock(self.state->mx);
self.state->result = result;
self.state->is_ready.store(std::memory_order_release);
// After this point finally() will no longer mutate state->receivers
}
// Could also use bulk executor here.
for (auto&& func : self->state->receivers) {
func(std::as_const(self->state->result));
}
});
}
LazyFuture<T> lazify() {
return { [self = *this] (Receiver<T> receiver) mutable noexcept {
self.finally(receiver);
}};
}
void finally(std::function<void(expected<T>)> func) {
if (state->is_ready.load(std::memory_order_acquire)) {
func(std::as_const(state->result));
return;
}
auto lk = std::unique_lock(state->mx);
if (state->is_ready.load(std::memory_order_relaxed)) {
lk.unlock();
func(std::as_const(state->result));
return;
}
state->receivers.push_back(func);
}
private:
struct State {
std::mutex mx;
std::condition_variable cv;
std::atomic<bool> is_ready;
std::optional<expected<T>> result;
std::vector<Receiver<T>> receivers;
};
std::shared_ptr<State> state = std::make_shared<State>();
};
template <typename T> SharedFuture(Future<T>) -> SharedFuture<T>;
template <typename T> SharedFuture(LazyFuture<T>) -> SharedFuture<T>;
template <
typename RealExec,
template<typename> typename Future = Future>
class EagerDependencyObliviousExecutorMixin {
public:
template <typename Continuation>
auto initially_execute(Continuation&& c) {
using R = decltype(c());
auto [fut, p] = Future<R>::make_promise_contract();
realExec()->execute([p = std::move(p), c] () mutable noexcept {
p.set_with(c);
});
return fut;
}
template <typename InFut, typename Continuation>
auto then_execute(Continuation&& c, InFut&& inFut) {
using R = decltype(c(inFut.get_expected()));
auto [fut, p] = Future<R>::make_promise_contract();
inFut.finally([this, p = std::move(p), c] (auto&& res) mutable noexcept {
realExec()->execute([p, c, res] () mutable noexcept {
p.set_with([&] { return c(res); });
});
});
return fut;
}
template <typename InFut, typename Continuation>
void finally_execute(Continuation&& c, InFut&& inFut) {
static_assert(std::is_void<decltype(c(inFut.get_expected()))>());
inFut.finally([this, c] (auto&& res) {
realExec()->execute([c, res] () noexcept {
c(res);
});
});
}
private:
RealExec* realExec() {
return static_cast<RealExec*>(this);
}
};
template <
typename RealExec,
template<typename> typename Future = LazyFuture>
class LazyDependencyObliviousExecutorMixin {
public:
template <typename Continuation>
auto initially_execute(Continuation&& c) {
using R = decltype(c());
return Future<R>{[this, c](Receiver<R> receiver) noexcept {
realExec()->execute([c, receiver] () noexcept {
receiver(c());
});
}};
}
template <typename InFut, typename Continuation>
auto then_execute(Continuation&& c, InFut&& inFut) {
using T = typename std::remove_reference_t<InFut>::value_type;
using R = decltype(c(std::declval<expected<T>>()));
return Future<R>{[this, c, inFut](Receiver<R> receiver) mutable noexcept {
inFut.finally([this, c, receiver] (expected<R> res) noexcept {
realExec()->execute([c, receiver, res] () noexcept {
receiver(c(res));
});
});
}};
}
template <typename InFut, typename Continuation>
void finally_execute(Continuation&& c, InFut&& inFut) {
using T = typename std::remove_reference_t<InFut>::value_type;
static_assert(std::is_void<decltype(c(std::declval<expected<T>>()))>());
inFut.finally([this, c] (expected<T> res) noexcept {
realExec()->execute([c, res] () noexcept{
c(res);
});
});
}
private:
RealExec* realExec() {
return static_cast<RealExec*>(this);
}
};
struct Executor : EagerDependencyObliviousExecutorMixin<Executor> {
void execute(std::function<void()>);
};
struct LazyExecutor : LazyDependencyObliviousExecutorMixin<LazyExecutor> {
void execute(std::function<void()>);
};
void func() {
Executor ex;
auto fut1 = ex.initially_execute([]{ return 1;});
auto fut2 = ex.then_execute([](expected<int>){ return 1;}, fut1);
auto fut3 = ex.then_execute([](expected<int>){ return 1;}, fut2);
ex.finally_execute([](expected<int>){}, fut3);
}
void func2() {
LazyExecutor ex;
auto fut1 = ex.initially_execute([]{ return 1;});
auto fut2 = ex.then_execute([](expected<int>){ return 1;}, fut1);
auto fut3 = ex.then_execute([](expected<int>){ return 1;}, fut2);
ex.finally_execute([](expected<int>){}, fut3);
}
void func3() {
Executor ex;
LazyExecutor lex;
auto fut1 = ex.initially_execute([]{ return 1;});
auto fut2 = ex.then_execute([](expected<int>){ return 1;}, fut1);
auto fut3 = lex.then_execute([](expected<int>){ return 1;}, fut2);
lex.finally_execute([](expected<int>){}, fut3);
}
void func4() {
Executor ex;
LazyExecutor lex;
auto fut1 = lex.initially_execute([]{ return 1;});
auto fut2 = lex.then_execute([](expected<int>){ return 1;}, fut1);
auto fut3 = ex.then_execute([](expected<int>){ return 1;}, fut2);
ex.finally_execute([](expected<int>){}, fut3);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment