flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:28:38 -05:00
commit b9c17f9506
59 changed files with 4984 additions and 0 deletions

17
tjp/core/future/Future.h Executable file
View File

@@ -0,0 +1,17 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "./custom/Future.h"
#include <type_traits>
#include <functional>
namespace tjp {
namespace core {
template<typename T> struct is_future : std::false_type {};
template<typename T> struct is_future<Future<T>> : std::true_type {};
template<> struct is_future<Future<void>> : std::true_type {};
} // namespace
} // namespace

205
tjp/core/future/Future.hpp Executable file
View File

@@ -0,0 +1,205 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.h"
#include "./custom/Future.hpp"
#include <tjp/core/algorithm/copy_of.hpp>
namespace tjp {
namespace core {
template<typename R, typename F>
Promise<R> &promise_of(Promise<R> &p, F &&f)
{
p.consume(f);
return p;
}
template<typename F>
auto future_of(F &&f)
{
using R_Ref = decltype(f());
using R = std::remove_reference_t<R_Ref>;
Promise<R> promise;
promise.consume(f);
return promise.get_future();
}
template<typename F, typename ... Args>
auto future_of(F &&f, Args && ... args)
{
using R_Ref = decltype(f(std::forward<Args>(args)...));
using R = std::remove_reference_t<R_Ref>;
Promise<R> promise;
promise.consume(f, std::forward<Args>(args)...);
return promise.get_future();
}
template<typename T>
auto future_of_value(T &&t)
{
using T_ = std::remove_reference_t<T>;
Promise<T_> promise;
promise.set_value(std::forward<T>(t));
return promise.get_future();
}
template<typename T>
Future<T> future_of_value(const T &t)
{
Promise<T> promise;
promise.set_value(t);
return promise.get_future();
}
inline
Future<void> future_of_value()
{
Promise<void> promise;
promise.set_value();
return promise.get_future();
}
template<typename T, typename E>
Future<T> future_of_exception(E &&e)
{
Promise<T> promise;
promise.set_exception(std::forward<E>(e));
return promise.get_future();
}
template<typename T, typename E>
Future<T> future_of_exception(const E &e)
{
return future_of_exception<T, E>(E(e));
}
template<typename F>
auto future_with(F &&f)
{
using R_Ref = decltype(f());
using R = std::remove_reference_t<R_Ref>;
if constexpr (is_future<R>::value)
{
using RV = typename R::value_type;
try
{
return f();
}
CORE_FUTURE_CATCH_BEGIN(e)
{
return future_of_exception<RV>(e);
}
CORE_FUTURE_CATCH_END
}
else
{
return future_of(f);
}
}
template<typename F, typename ... Args>
auto future_with(F &&f, Args && ...args)
{
using R_Ref = decltype(f(std::forward<Args>(args)...));
using R = std::remove_reference_t<R_Ref>;
if constexpr (is_future<R>::value)
{
using RV = typename R::value_type;
try
{
return f(std::forward<Args>(args)...);
}
CORE_FUTURE_CATCH_BEGIN(e)
{
return future_of_exception<RV>(e);
}
CORE_FUTURE_CATCH_END
}
else
{
return future_of(f, std::forward<Args>(args)...);
}
}
template<typename T>
struct AsFuture_
{
T v;
template<typename U>
operator Future<U>()
{
return future_of_value<U>(std::move(v));
}
};
template<>
struct AsFuture_<void>
{
template<typename U>
operator Future<U>()
{
return future_of_value();
}
} ;
template<typename T>
AsFuture_<T> as_future(T &&t)
{
return AsFuture_<std::remove_reference_t<T>> { std::forward<T>(t) };
}
inline
AsFuture_<void> as_future()
{
return AsFuture_<void> { };
}
template<typename T>
struct AsFutureException_
{
T v;
template<typename U>
operator Future<U>()
{
return future_of_exception<U>(std::move(v));
}
};
template<typename T>
AsFutureException_<T> as_future_exception(T &&t)
{
return AsFutureException_<std::remove_reference_t<T>> { std::forward<T>(t) };
}
//template<typename F>
//auto future_with(F f) -> decltype(f()) {
// using R = typename decltype(f())::value_type;
// try {
// return f();
// }
// catch (Exception &e) {
// return future_of_exception<R>(copy_of(e));
// }
//}
//template<typename R, typename ...T, typename std::enable_if<!is_future<R>::value, R>::type*>
//auto with(std::function<R(T&&...)> &&f) {
// return f;
//}
//
} // namespace
} // namespace

4
tjp/core/future/FutureChain.h Executable file
View File

@@ -0,0 +1,4 @@
// TJP COPYRIGHT HEADER
#pragma once

177
tjp/core/future/FutureChain.hpp Executable file
View File

@@ -0,0 +1,177 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "FutureChain.h"
#include "Futures.hpp"
#include <tjp/core/ptr/Ptr.hpp>
#include <type_traits>
#include <tjp/core/algorithm/copy_of.hpp>
#include <tjp/core/threads/Lock.hpp>
#include <tjp/core/debug/Debug.h>
namespace tjp {
namespace core {
namespace future_chain_detail {
struct FutureParent
{
virtual ~FutureParent() {}
} ;
template<typename FutureGeneratorParameter>
struct FutureExecutor : FutureParent
{
virtual void execute(const FutureGeneratorParameter &parameter) = 0;
} ;
template<typename FutureGeneratorParameter, typename FutureGenerator>
struct FutureNode : FutureExecutor<FutureGeneratorParameter>
{
FutureGenerator futureGenerator;
typedef decltype(futureGenerator(FutureGeneratorParameter())) FutureType;
typedef Promise<typename FutureType::value_type> PromiseType;
typedef FutureExecutor<FutureType> Child;
WeakPtr<FutureNode> self;
StrongPtr<FutureParent> parent;
Future<void> next;
PromiseType promise;
FutureType future;
WeakPtr<Child> link;
FutureNode(FutureGenerator &&futureGenerator_, const StrongPtr<FutureParent> &parent_) :
futureGenerator(std::move(futureGenerator_)),
parent(parent_)
{
future = promise.get_future();
}
~FutureNode()
{
xDebugLine();
}
static auto generate(FutureGenerator &&futureGenerator_, const StrongPtr<FutureParent> &parent_)
{
auto v = strong<FutureNode<FutureGeneratorParameter, FutureGenerator>>(std::move(futureGenerator_), parent_);
v->self = weak(v);
return v;
}
template<typename F,
typename std::enable_if<std::is_invocable<F, FutureType>::value, F>::type* = nullptr
>
auto add(F &&nextFutureGenerator)
{
auto child = FutureNode<FutureType, F>::generate(std::move(nextFutureGenerator), strong(self));
if (future.is_set())
{
child->execute(future);
return child;
}
link = weak(child);
return child;
}
template<typename F,
typename std::enable_if<std::is_invocable<F>::value, F>::type* = nullptr
>
auto add(F &&nextFutureGenerator)
{
return add(
[nextFutureGenerator=std::move(nextFutureGenerator)](auto &&f) {
f.get();
return nextFutureGenerator();
}
);
}
void execute(const FutureGeneratorParameter &parameter) override
{
parent = nullptr;
try
{
next = futureGenerator(copy_of(parameter)).then(
[self_=this->self, this](auto &&f) {
if (auto self = strong(self_))
{
promise.consume([](auto &&f){ return f.get(); }, std::move(f));
executeChildren(future);
}
}
);
}
catch (Exception &e)
{
promise.set_exception(copy_of(e));
executeChildren(future);
}
}
void executeChildren(const FutureType &f)
{
if (auto child = strong(this->link))
{
executeChild(child, f);
}
}
void executeChild(const StrongPtr<Child> &child, const FutureType &f)
{
child->execute(f);
}
FutureType get_future()
{
return future.then([self = strong(this->self)](auto &&f) {
return f.get();
});
}
};
inline
auto FutureChain()
{
auto empty = [](Future<void> &&) {
return future_of_value();
};
auto chain =
FutureNode<Future<void>, decltype(empty)>::generate(
std::move(empty),
nullptr
);
chain->execute(Future<void>());
return chain;
}
} // namespace future_chain_detail
template<typename A, typename B>
auto future_chain_(A &&a, B &&b)
{
return a->add(std::forward<B>(b));
} ;
template<typename A, typename B, typename ...T>
auto future_chain_(A &&a, B &&b, T &&...t)
{
return future_chain_(a->add(std::forward<B>(b)), std::forward<T>(t)...);
} ;
template<typename ...T>
auto future_chain(T &&...t)
{
return future_chain_(future_chain_detail::FutureChain(), std::forward<T>(t)...)->get_future();
} ;
} // namespace
} // namespace

View File

@@ -0,0 +1,79 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <tjp/core/future/Future.hpp>
#include <tjp/core/ptr/Ptr.hpp>
#include <tjp/core/ptr/strong_of.hpp>
#include <tjp/core/ptr/strong_emplace.hpp>
namespace tjp::core {
namespace futures::v2 {
struct FutureErase
{
StrongPtr<Future<void>> future;
FutureErase()
{}
template<typename T>
FutureErase(Future<T> &&future_)
{
set(std::move(future_));
}
~FutureErase()
{
clear();
}
template<typename T>
void set(Future<T> &&future_)
{
strong_emplace(future);
*future = future_.then([future_ = weak(future)](auto &&f){
if (auto future = strong(future_))
(*future) = {};
});
if (future->is_set())
future = {};
}
template<typename T>
void operator =(Future<T> &&future_)
{
return set(std::move(future_));
}
void clear()
{
future.reset();
}
bool has_value () const
{
return future && future->valid();
}
bool empty() const
{
return !has_value();
}
Future<void> get_future()
{
auto future_ = future;
return future_ ?
*future_ : future_of_value();
}
} ;
} // namespace
using FutureErase = futures::v2::FutureErase;
} // namespace

58
tjp/core/future/FutureEvent.hpp Executable file
View File

@@ -0,0 +1,58 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <functional>
#include <tjp/core/log/Log.h>
namespace tjp {
namespace core {
template<typename T>
struct FutureEvent
{
typedef Future<T> F;
bool waiting_ = false;
Future<void> dependency;
std::function<void(F &&f)> on;
void set(Future<T> &&future_) {
waiting_ = true;
dependency = future_.then([this](F &&f) {
reset();
if (on)
on(std::forward<F>(f));
});
}
bool waiting() const
{
return waiting_;
}
void reset ()
{
dependency = {};
waiting_ = false;
}
FutureEvent(const FutureEvent &) = delete;
FutureEvent()
{
sLogDebug("core::future::FutureEvent", this);
}
~FutureEvent ()
{
sLogDebug("core::future::FutureEvent", this);
dependency = {};
}
} ;
} // namespace
} // namespace

10
tjp/core/future/FutureResult.h Executable file
View File

@@ -0,0 +1,10 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp::core {
template<typename T>
struct FutureResult;
} // namespace

View File

@@ -0,0 +1,5 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "FutureResult_v1.hpp"

View File

@@ -0,0 +1,157 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include <tjp/core/containers/Optional.hpp>
namespace tjp::core {
namespace futures::v1 {
template<typename T>
struct FutureResult_
{
enum Mode {
None,
Running,
Finished
};
Mode mode = None;
Future<T> future;
Optional<Exception> exception;
void reset ()
{
mode = None;
future = {};
exception.reset();
}
operator bool()
{
return isFinished ();
}
bool isInitiated ()
{
return mode != None;
}
void update ()
{
if (mode != Running)
return ;
if (future.valid() && future.is_set())
{
mode = Finished;
try
{
future.get();
}
catch (Exception &e)
{
exception = e;
}
}
}
bool isFinished ()
{
update();
return mode == Finished;
}
bool isRunning ()
{
update();
return mode == Running;
}
Future<T> &set(Future<T> &&future_)
{
mode = Running;
exception = {};
future = std::move(future_);
return future;
}
Future<T> &operator =(Future<T> &&future_)
{
return set(future_);
}
bool finished ()
{
return isFinished();
}
bool succeeded ()
{
return isFinished() && !exception.has_value();
}
bool failed ()
{
return isFinished() && exception.has_value();
}
bool has_value ()
{
return succeeded();
}
bool has_exception()
{
return exception.has_value();
}
const Exception &get_exception()
{
return *exception;
}
bool empty()
{
return !isFinished();
}
} ;
template<typename T>
struct FutureResult : FutureResult_<T>
{
using Super = FutureResult_<T>;
Future<T> &operator =(Future<T> &&future_)
{
return this->set(std::move(future_));
}
const T &get()
{
return this->future.get();
}
} ;
template<>
struct FutureResult<void> : FutureResult_<void>
{
using Super = FutureResult_<void>;
Future<void> &operator =(Future<void> &&future_)
{
return this->set(std::move(future_));
}
} ;
} // namespace
using futures::v1::FutureResult;
} // namespace

View File

@@ -0,0 +1,183 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include "FutureErase.hpp"
#include <tjp/core/containers/Optional.hpp>
namespace tjp::core {
namespace futures::v2 {
template<typename T>
struct ExpectedValue
{
Optional<T> value;
Optional<Exception> exception;
ExpectedValue(Future<T> &&f)
{
try
{
value.emplace(std::move(f.get()));
}
catch (const Exception &e)
{
exception.emplace(e);
}
}
const T &get()
{
if (exception)
throw *exception;
return *value;
}
T &&move()
{
if (exception)
throw *exception;
return std::move(*value);
}
} ;
template<>
struct ExpectedValue<void>
{
Optional<Exception> exception;
ExpectedValue(Future<void> &&f) {
try
{
f.get();
}
catch (const Exception &e)
{
exception.emplace(e);
}
}
void get()
{
if (exception)
throw *exception;
}
} ;
template<typename T>
struct FutureResult_
{
FutureErase future;
Optional<ExpectedValue<T>> expected;
void reset ()
{
future.clear();
expected.reset();
}
operator bool()
{
return has_value();
}
bool isInitiated ()
{
return isRunning() || isFinished();
}
bool isFinished ()
{
return finished();
}
bool isRunning ()
{
return future.has_value();
}
void operator =(Future<T> &&future_)
{
set(std::move(future_));
}
void set(Future<T> &&future_)
{
expected.reset();
future = future_.then([this](auto &&f) {
expected.emplace(std::move(f));
});
}
bool finished ()
{
return expected;
}
bool succeeded ()
{
return expected && !expected->exception;
}
bool failed ()
{
return expected && expected->exception;
}
bool has_exception ()
{
return expected && expected->exception;
}
bool has_value ()
{
return succeeded();
}
const Exception &get_exception()
{
return *expected->exception;
}
bool empty()
{
return !expected;
}
} ;
template<typename T>
struct FutureResult : FutureResult_<T>
{
using Super = FutureResult_<T>;
void operator =(Future<T> &&future_)
{
set(std::move(future_));
}
const T &get()
{
return this->expected->get();
}
} ;
template<>
struct FutureResult<void> : FutureResult_<void>
{
using Super = FutureResult_<void>;
void operator =(Future<void> &&future_)
{
set(std::move(future_));
}
} ;
} // namespace
using futures::v2::FutureResult;
} // namespace

View File

@@ -0,0 +1,49 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <tjp/core/exception/NullPointer.hpp>
namespace tjp {
namespace core {
template<typename U>
auto future_strong(U &&f)
{
return f.then([](auto &&g) {
using T = typename std::remove_reference<decltype(g.get())>::type;
return strong<T>(std::move(g.get()));
});
}
template<typename T, typename U>
auto future_strong(U &&f)
{
return f.then([](auto &&g) {
return strong<T>(std::move(g.get()));
});
}
template<typename U>
auto future_strong_value(U &&f)
{
return f.then([](auto &&g) {
if (auto ptr = g.get())
return *ptr;
throw exceptions::NullPointer();
});
}
template<typename U>
auto future_strong_with(U &&ptr)
{
if (ptr)
return *ptr;
throw exceptions::NullPointer();
}
} // namespace
} // namespace

312
tjp/core/future/Futures.cpp Executable file
View File

@@ -0,0 +1,312 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/header_only/compile.h>
#ifdef TJP_CORE_HEADER_ONLY
#pragma once
#endif
#include "Futures.hpp"
#include <tjp/core/threads/Lock.hpp>
#include <tjp/core/containers/List.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/Optional.hpp>
#include <tjp/core/threads/Atomic.h>
#include <tjp/core/algorithm/vector_erase_if_value.hpp>
#include <tjp/core/assert/debug_assert.h>
#include <tjp/core/containers/Map.hpp>
#include <tjp/core/containers/List.hpp>
#include <tjp/core/debug/Stack.h>
#include <tjp/core/string/String.h>
#include <tjp/core/algorithm/map_erase.hpp>
#include <tjp/core/time/Time.h>
#include <tjp/core/log/Log.h>
#include <tjp/core/log/LogOf.h>
//#define DEBUG_FUTURE_LEAKS_BUT_PROBLEMS_WITH_THIS
#ifdef DEBUG_FUTURE_LEAKS_BUT_PROBLEMS_WITH_THIS
#include <execinfo.h>
#include <sstream>
#endif
namespace tjp {
namespace core {
#ifdef DEBUG_FUTURE_LEAKS_BUT_PROBLEMS_WITH_THIS
static Atomic<size_t> totalOutstandingFutures = 0;
struct StackFrames {
void* trace[64];
int size;
} ;
String toString(StackFrames &f)
{
char** messages = NULL;
std::ostringstream out;
messages = backtrace_symbols( f.trace, f.size );
for( int i = 2; i < f.size; ++i ) {
out << "\t" << messages[i] << std::endl;
}
free (messages);
return out.str();
}
struct Leak {
time::Time then;
StackFrames stack;
WeakPtr<FuturePersist> future;
} ;
Map<void *, Leak> outstandingFutures;
time::Time lastLeaker = 0;
static void debug_log();
static void debug_futureCreated(const StrongPtr<FuturePersist> &future)
{
totalOutstandingFutures++;
auto &leak = outstandingFutures[ptr_of(future)];
leak.then = time::now();
leak.stack.size = backtrace( leak.stack.trace, 16 );
leak.future = weak(future);
sLogRelease("core::futures", logVar(totalOutstandingFutures));
debug_log();
}
static void debug_futureFinished(void *fp)
{
totalOutstandingFutures--;
auto erased_leak = map_erase(outstandingFutures, fp);
debug_assert(erased_leak);
sLogDebug("core::futures", logVar(totalOutstandingFutures));
debug_log();
}
void debug_log()
{
auto now = time::now();
if (now - lastLeaker > 5.0)
{
lastLeaker = now;
for (auto &[p, leak]: outstandingFutures)
{
auto diff = now - leak.then;
if (diff > 10.0)
{
sLogRelease("core::futures::leak", logVar(leak.future.use_count()) << logVar(diff) << logVar(p) << logVar(toString(leak.stack)));
}
}
}
}
#else
#define debug_futureCreated(x)
#define debug_futureFinished(x)
#endif
struct Futures::Internal {
mutable Mutex mutex;
List<StrongPtr<FuturePersist>> futures;
virtual void add(StrongPtr<Futures::Internal> &self, const StrongPtr<FuturePersist> &future)
{
StrongPtr<FuturePersist> r;
{
auto lock = lock_of(mutex);
r = futures.emplace_back(future);
debug_futureCreated(future);
}
r->inject([weak=weak(self)](auto *fp) {
if (auto self = strong(weak))
self->onFuture(fp);
});
}
void onFuture_noLock(void *fp)
{
auto erased = vector_erase_if_value(
futures,
[fp](auto &v) {
return ptr_of(v) == fp;
}
);
debug_assert(erased);
debug_futureFinished(fp);
}
virtual void onFuture(void *fp)
{
auto lock = lock_of(mutex);
onFuture_noLock(fp);
}
virtual void clear ()
{
auto l = lock_of(mutex);
futures.clear();
}
bool empty() const
{
auto l = lock_of(mutex);
return futures.empty();
}
} ;
struct FuturesEvent_Internal : Futures::Internal {
using Super = Futures::Internal;
Event event;
void onFuture(void *fp) override
{
Super::onFuture(fp);
event.notify_all();
}
void clear () override
{
auto l = lock_of(mutex);
futures.clear();
event.notify_all();
}
void wait()
{
while (!empty())
{
Event::Mutex m;
auto l = lock_of(m);
event.wait(l);
}
}
} ;
struct FuturesFuture_Internal : Futures::Internal
{
using Super = Futures::Internal;
bool shouldSetFinished = false;
Promise<void> finished;
void onFuture(void *fp) override
{
Optional<Promise<void>> finished_;
{
auto lock = lock_of(mutex);
Super::onFuture_noLock(fp);
if (shouldSetFinished && futures.empty())
{
// TODO: promises should have a move constructor
finished_.emplace(std::move(finished));
finished = {};
}
}
if (finished_)
finished_->set_value();
}
Future<void> get_future(StrongPtr<Futures::Internal> &self)
{
auto l = lock_of(mutex);
if (futures.empty())
return future_of_value();
shouldSetFinished = true;
return finished.get_future()
.then([self](auto &&f) {
return f.get();
});
}
} ;
// ----------
struct Futures::NoConstructInternal {};
TJP_CORE_HEADER_ONLY_INLINE
Futures::Futures (NoConstructInternal)
{
}
TJP_CORE_HEADER_ONLY_INLINE
Futures::Futures ()
{
internal = strong<Internal>();
}
TJP_CORE_HEADER_ONLY_INLINE
Futures::~Futures ()
{
}
TJP_CORE_HEADER_ONLY_INLINE
void Futures::add(const StrongPtr<FuturePersist> &future)
{
internal->add(internal, future);
}
TJP_CORE_HEADER_ONLY_INLINE
bool Futures::empty() const
{
return internal->empty();
}
TJP_CORE_HEADER_ONLY_INLINE
void Futures::clear()
{
internal->clear();
}
// ------------------
TJP_CORE_HEADER_ONLY_INLINE
FuturesEvent::FuturesEvent() :
Super(Super::NoConstructInternal{})
{
internal = strong<FuturesEvent_Internal>();
}
TJP_CORE_HEADER_ONLY_INLINE
void FuturesEvent::wait ()
{
strong_ptr_cast<FuturesEvent_Internal>(internal)->wait();
}
// ------------------
TJP_CORE_HEADER_ONLY_INLINE
FuturesFuture::FuturesFuture() :
Super(Super::NoConstructInternal{})
{
internal = strong<FuturesFuture_Internal>();
}
TJP_CORE_HEADER_ONLY_INLINE
Future<void> FuturesFuture::get_future()
{
return strong_ptr_cast<FuturesFuture_Internal>(internal)->get_future(internal);
}
} // namespace
} // namespace

16
tjp/core/future/Futures.h Executable file
View File

@@ -0,0 +1,16 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp {
namespace core {
struct FuturePersist;
template<typename T>
struct FuturePersistTyped;
struct Futures;
} // namespace
} // namespace

87
tjp/core/future/Futures.hpp Executable file
View File

@@ -0,0 +1,87 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Futures.h"
#include "Future.hpp"
#include <tjp/core/ptr/Ptr.hpp>
namespace tjp::core {
struct FuturePersist
{
virtual ~FuturePersist () {}
virtual void inject(std::function<void(FuturePersist *)> &&t) = 0;
} ;
template<typename T>
struct FuturePersistTyped : FuturePersist
{
Future<T> f;
FuturePersistTyped(const Future<T> &f_) :
f(f_)
{}
void inject(std::function<void(FuturePersist *)> &&t) override
{
f = f.then([t=std::move(t), this](auto &&f_) {
f_.wait();
t(this);
return f_.get();
});
}
} ;
struct Futures
{
struct Internal;
StrongPtr<Internal> internal;
struct NoConstructInternal;
Futures (NoConstructInternal);
Futures ();
~Futures ();
void add(const StrongPtr<FuturePersist> &future);
template<typename T>
Future<T> add(const Future<T> &future)
{
auto persisted = strong<FuturePersistTyped<T>>(future);
add(persisted);
return persisted->f;
}
bool empty() const;
void clear();
} ;
struct FuturesEvent : Futures
{
using Super = Futures;
FuturesEvent ();
void wait ();
} ;
struct FuturesFuture : Futures
{
using Super = Futures;
FuturesFuture ();
Future<void> get_future();
} ;
} // namespace
#ifdef TJP_CORE_HEADER_ONLY
#include "Futures.cpp"
#endif

15
tjp/core/future/PromiseLocked.h Executable file
View File

@@ -0,0 +1,15 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp {
namespace core {
template<typename T>
struct PromiseLocked;
template<typename R, typename F>
PromiseLocked<R> &promise_of_and_reset(PromiseLocked<R> &p, F &&f);
} // namespace
} // namespace

168
tjp/core/future/PromiseLocked.hpp Executable file
View File

@@ -0,0 +1,168 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "PromiseLocked.h"
#include "Future.hpp"
#include <tjp/core/type_traits/is_callable.hpp>
namespace tjp {
namespace core {
template<typename T>
struct PromiseLocked
{
Mutex m;
bool owned = false;
Promise<T> promise;
std::tuple<bool, Future<T>> own_future()
{
auto l = lock_of(m);
if (promise.is_set() || owned)
return { false, promise.get_future() };
owned = true;
return { true, promise.get_future() };
}
template <typename U=T,
typename std::enable_if<!std::is_void<U>::value, U>::type* = nullptr
>
void set_value (U &&t)
{
auto l = lock_of(m);
debug_assert(owned);
promise.set_value(t);
owned = false;
}
template <typename U=T,
typename std::enable_if<!std::is_void<U>::value, U>::type* = nullptr
>
void set_value (const U &t)
{
auto l = lock_of(m);
debug_assert(owned);
promise.set_value(t);
owned = false;
}
template <typename U=void,
typename std::enable_if<std::is_void<U>::value, U>::type* = nullptr
>
void set_value ()
{
auto l = lock_of(m);
debug_assert(owned);
promise.set_value();
owned = false;
}
void set_exception (Exception &&e)
{
auto l = lock_of(m);
debug_assert(owned);
promise.set_exception(std::move(e));
owned = false;
}
template<typename F,
typename std::enable_if<std::is_invocable<F>::value>::type* = nullptr
>
void consume (F &&f)
{
auto l = lock_of(m);
debug_assert(owned);
promise.consume(std::forward<F>(f));
owned = false;
}
template<typename F,
typename std::enable_if<std::is_invocable<F>::value>::type* = nullptr
>
void reset_consume (F &&f)
{
debug_assert(owned);
Promise<T> o = reset();
o.consume(std::forward<F>(f));
}
template<typename F,
typename std::enable_if<!std::is_invocable<F>::value>::type* = nullptr
>
void consume (F &&f)
{
consume([&f]() { return f.get(); });
}
template<typename F,
typename std::enable_if<!std::is_invocable<F>::value>::type* = nullptr
>
void reset_consume (F &&f)
{
reset_consume([&f]() { return f.get(); });
}
template<typename V>
void consume_value (const V &v)
{
auto l = lock_of(m);
debug_assert(owned);
set_value(v);
owned = false;
}
template<typename V>
void reset_consume_value (const V &v)
{
debug_assert(owned);
Promise<T> o = reset();
o.set_value(v);
}
void release ()
{
auto l = lock_of(m);
debug_assert(owned);
owned = false;
}
Promise<T> reset ()
{
auto l = lock_of(m);
owned = false;
auto r = std::move(promise);
promise = {};
return r;
}
} ;
template<typename R, typename F>
PromiseLocked<R> &promise_of_and_reset(PromiseLocked<R> &p, F &&f)
{
p.reset_consume(std::forward<F>(f));
return p;
}
template<typename R, typename V>
PromiseLocked<R> &promise_of_and_reset_value(PromiseLocked<R> &p, const V &v)
{
p.reset_consume_value(v);
return p;
}
} // namespace
} // namespace

View File

@@ -0,0 +1,21 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <tjp/core/ptr/Ptr.h>
namespace tjp {
namespace core {
template<typename T>
struct PromiseOfFutures;
template<>
struct PromiseOfFutures<void>;
template<typename T>
using PromiseOfFuturesPtr = StrongPtr<PromiseOfFutures<T>>;
} // namespace
} // namespace

View File

@@ -0,0 +1,228 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "PromiseOfFutures.h"
#include "Futures.hpp"
#include <tjp/core/ptr/Ptr.hpp>
#include <tjp/core/log/Log.h>
#include <tjp/core/log/LogOf.h>
#include <atomic>
namespace tjp {
namespace core {
// TODO: PromiseOfFutures should go away and instead just have FuturesFuture
// TODO: this class can cause a memory leak, if the underlying future is not triggered
template<typename T>
struct PromiseOfFutures
{
typedef PromiseOfFutures<T> Self;
typedef StrongPtr<Self> Instance;
WeakPtr<Self> self;
T value;
StrongPtr<Exception> exception;
Futures futures;
Promise<T> promise;
std::atomic<size_t> outstanding = 1;
void onCompletion()
{
auto outstanding_ = --outstanding;
fLogDebug(logOfThis(this) << "onCompletion " << outstanding_);
if (outstanding_ == 0)
{
if (exception)
{
xLogDebug(logOfThis(this) << "exception");
promise.set_exception(Exception(*exception));
}
else
{
xLogDebug(logOfThis(this) << "value");
promise.set_value(value);
}
}
}
template<typename FT>
auto add(FT &&f)
{
auto outstanding_ = ++outstanding;
fLogDebug(logOfThis(this) << "add " << outstanding_);
return futures.add(f.template then<>([self_=this->self, this](auto &&f_)
{
if (auto self = strong(self_))
{
try
{
f_.get();
}
catch (Exception &e)
{
exception = strong<Exception>(e);
}
onCompletion();
}
}));
}
auto get_promise ()
{
return promise;
}
auto get_future ()
{
onCompletion();
return promise.get_future()
.then([self=strong(this->self)](auto &&f) {
return f.get();
})
.then([](auto &&f) { return f.get(); });
}
template<typename F>
void consume(F &&f)
{
try
{
value = f.get();
}
catch (Exception &e)
{
exception = strong<Exception>(e);
}
}
template<typename F>
void consume_throw_exception(F &&f)
{
value = f.get();
}
void set(const T &t)
{
value = t;
}
static StrongPtr<PromiseOfFutures> generate()
{
auto v = strong<PromiseOfFutures>();
v->self = weak(v);
return v;
}
public:
PromiseOfFutures() {}
} ;
template<>
struct PromiseOfFutures<void>
{
typedef PromiseOfFutures<void> Self;
typedef StrongPtr<Self> Instance;
WeakPtr<Self> self;
StrongPtr<Exception> exception;
Futures futures;
Promise<void> promise;
size_t outstanding = 1;
void onCompletion()
{
auto outstanding_ = --outstanding;
fLogDebug(logOfThis(this) << "onCompletion " << outstanding_);
if (outstanding_ == 0)
{
if (exception)
{
xLogDebug(logOfThis(this) << "exception");
promise.set_exception(Exception(*exception));
}
else
{
xLogDebug(logOfThis(this) << "value");
promise.set_value();
}
}
}
template<typename FT>
auto add(FT &&f)
{
auto outstanding_ = ++outstanding;
(void)outstanding_;
fLogDebug(logOfThis(this) << "add " << outstanding_);
return futures.add(f.template then<>([self_=this->self, this](auto &&f_)
{
if (auto self = strong(self_))
{
try
{
f_.get();
}
catch (Exception &e)
{
exception = strong<Exception>(e);
}
onCompletion();
}
}));
}
auto get_future ()
{
onCompletion();
return promise.get_future().then([self=strong(this->self)](auto &&f) {
return f.get();
});
}
template<typename F>
void consume(F &&f)
{
try
{
f.get();
}
catch (Exception &e)
{
exception = strong<Exception>(e);
}
}
template<typename F>
void consume_throw_exception(F &&f)
{
f.get();
}
static StrongPtr<PromiseOfFutures> generate()
{
auto v = strong<PromiseOfFutures>();
v->self = weak(v);
return v;
}
public:
PromiseOfFutures() {}
} ;
} // namespace
} // namespace

37
tjp/core/future/WeakFuture.hpp Executable file
View File

@@ -0,0 +1,37 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
namespace tjp {
namespace core {
// this may need to be strong this
template<typename T>
struct WeakFuture
{
typedef Future<T> F;
F f;
u8 number = 0;
void set(Future<T> &&f_)
{
auto number_ = ++number;
f = f_.then([this, number_](auto &&) {
if (number_ == number)
f = {};
});
};
auto &operator =(Future<T> &&f_)
{
set(std::move(f_));
return *this;
}
} ;
} // namespace
} // namespace

12
tjp/core/future/WorkPromise.h Executable file
View File

@@ -0,0 +1,12 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp {
namespace core {
template<typename W, typename T>
struct WorkPromise;
} // namespace
} // namespace

27
tjp/core/future/WorkPromise.hpp Executable file
View File

@@ -0,0 +1,27 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "PromiseOfFutures.hpp"
namespace tjp {
namespace core {
template<typename W, typename T>
struct WorkPromise : PromiseOfFutures<T>
{
typedef PromiseOfFutures<T> Super;
W work;
static StrongPtr<WorkPromise> generate()
{
auto v = strong<WorkPromise>();
v->self = weak(strong_ptr_cast<Super>(v));
return v;
}
} ;
} // namespace
} // namespace

View File

@@ -0,0 +1,257 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/future/FutureChain.hpp>
#include <tjp/core/iterators/range.hpp>
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/timer/Timer.hpp>
namespace tjp {
namespace core {
namespace futures {
namespace promise_of_futures_test {
SCENARIO("core::future::future_chain" )
{
GIVEN("a void promise")
{
WHEN("two different futures using functional auto no_f and exception")
{
Promise<int> a;
Promise<std::string> b;
Promise<int> c;
Promise<int> d;
int i = 32;
int x = 0;
auto future = future_chain(
[&a, i]() {
return a.get_future();
},
[&b, &x, i]() {
x++;
return b.get_future();
},
[&c, &x, i]() {
x++;
return c.get_future();
},
[&d, &x, i]() {
x++;
return d.get_future();
}
);
THEN("waits for sub futures")
{
Timer timer;
x = 0;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_exception(Exception { "Failed"});
b.set_value("hello");
// c.set_value(1);
// d.set_value(2);
});
future.wait();
threadA.join();
REQUIRE(x == 0);
REQUIRE_THROWS(future.get());
}
}
WHEN("no futures")
{
auto future = future_chain_detail::FutureChain()->get_future();
future.wait();
REQUIRE(true);
}
WHEN("one future")
{
Promise<void> a;
auto chain =
future_chain_detail::FutureChain()->add([&]() {
return a.get_future();
})
;
THEN("waits for result")
{
auto future = chain->get_future();
chain = nullptr;
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value();
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
THEN("if future is discarded chain deletes")
{
auto future = chain->get_future();
auto p = weak(chain);
chain = nullptr;
future = {};
REQUIRE(strong(p) == nullptr);
}
}
WHEN("two futures")
{
Promise<void> a, b;
auto chain = future_chain_detail::FutureChain()
->add([&]() {
return a.get_future();
})
->add([&](auto &&f) {
return b.get_future();
})
;
THEN("waits for sub futures")
{
auto future = chain->get_future();
chain = nullptr;
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value();
b.set_value();
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
}
WHEN("two different futures")
{
Promise<int> a;
Promise<std::string> b;
auto chain = future_chain_detail::FutureChain()
->add([&]() {
return a.get_future();
})
->add([&](Future<int> &&f) {
return b.get_future();
})
;
THEN("waits for sub futures")
{
auto future = chain->get_future();
chain = nullptr;
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value(1);
b.set_value("hello");
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
}
WHEN("two different futures using functional")
{
Promise<int> a;
Promise<std::string> b;
auto i = 32;
auto future = future_chain(
[&a, i]() {
return a.get_future();
},
[&b, i](Future<int> &&f) {
return b.get_future();
}
);
THEN("waits for sub futures")
{
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value(1);
b.set_value("hello");
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
}
WHEN("two different futures using functional auto")
{
Promise<int> a;
Promise<std::string> b;
int i = 32;
auto future = future_chain(
[&a, i]() {
return a.get_future();
},
[&b, i](auto &&f) {
return b.get_future();
}
);
THEN("waits for sub futures")
{
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value(1);
b.set_value("hello");
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
}
}
}
} // namespace
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,39 @@
// TJP COPYRIGHT HEADER
#include "../FutureErase.hpp"
#include <tjp/core/testing/catch.hpp>
namespace tjp::core::futures {
namespace {
SCENARIO("core::futures::FutureErase")
{
GIVEN("a Futures")
{
FutureErase futures;
WHEN("adding a future that resolves afterwards")
{
Promise<void> promise;
futures.set(promise.get_future());
REQUIRE(!futures.empty());
promise.set_value();
REQUIRE(futures.empty());
}
WHEN("adding a future that's already resolved")
{
Promise<void> promise;
promise.set_value();
futures.set(promise.get_future());
REQUIRE(futures.empty());
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,97 @@
// TJP COPYRIGHT HEADER
#include "../FutureResult.hpp"
#include <tjp/core/testing/catch.hpp>
namespace tjp::core::futures {
namespace {
SCENARIO("core::futures::FutureResult")
{
GIVEN("a FutureResult")
{
FutureResult<void> futures;
WHEN("adding a future that resolves afterwards")
{
Promise<void> promise;
futures.set(promise.get_future());
REQUIRE(futures.empty());
promise.set_value();
REQUIRE(!futures.empty());
}
WHEN("adding a future that's already resolved")
{
Promise<void> promise;
promise.set_value();
futures.set(promise.get_future());
REQUIRE(!futures.empty());
}
}
GIVEN("a FutureResult")
{
FutureResult<int> futures;
WHEN("adding a future that resolves afterwards")
{
Promise<int> promise;
futures.set(promise.get_future());
REQUIRE(futures.empty());
promise.set_value(42);
REQUIRE(!futures.empty());
REQUIRE(futures.get() == 42);
}
WHEN("adding a future that's already resolved")
{
Promise<int> promise;
promise.set_value(42);
futures.set(promise.get_future());
REQUIRE(!futures.empty());
REQUIRE(futures.get() == 42);
}
}
GIVEN("a FutureResult")
{
FutureResult<void> futures;
WHEN("adding a future that resolves afterwards")
{
Promise<void> promise;
futures.set(promise.get_future());
REQUIRE(futures.empty());
REQUIRE(!futures.has_exception());
promise.set_exception(Exception { "abcd" });
REQUIRE(!futures.empty());
REQUIRE(!futures.has_value());
REQUIRE(futures.has_exception());
REQUIRE(futures.get_exception().what() == "abcd");
}
WHEN("adding a future that's already resolved")
{
Promise<void> promise;
promise.set_exception(Exception { "abcd" });
futures.set(promise.get_future());
REQUIRE(!futures.empty());
REQUIRE(!futures.has_value());
REQUIRE(futures.has_exception());
REQUIRE(futures.get_exception().what() == "abcd");
}
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,23 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/future/PromiseLocked.hpp>
namespace tjp {
namespace core {
namespace {
static void futures_promise_locked_test ()
{
PromiseLocked<int> pi;
pi.consume(future_of_value((int)1));
pi.consume([]() { return (int)1; });
PromiseLocked<void> pv;
pv.consume(future_of_value());
pv.consume([]() {});
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,124 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/future/PromiseOfFutures.hpp>
#include <tjp/core/iterators/range.hpp>
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/timer/Timer.hpp>
namespace tjp {
namespace core {
namespace futures {
namespace promise_of_futures_test {
SCENARIO("promise of future" )
{
GIVEN("a void promise")
{
auto promise = PromiseOfFutures<void>::generate();
WHEN("no futures")
{
auto future = promise->get_future();
future.wait();
REQUIRE(true);
}
WHEN("one future")
{
Promise<void> a;
promise->add(a.get_future());
THEN("waits for result")
{
auto future = promise->get_future();
promise = nullptr;
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value();
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
THEN("if future is discarded promise deletes")
{
auto future = promise->get_future();
auto p = weak(promise);
promise = nullptr;
future = {};
REQUIRE(strong(p) == nullptr);
}
}
WHEN("two futures")
{
Promise<void> a, b;
promise->add(a.get_future());
promise->add(b.get_future());
THEN("waits for sub futures")
{
auto future = promise->get_future();
promise = nullptr;
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value();
b.set_value();
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
}
WHEN("two different futures")
{
Promise<int> a;
Promise<std::string> b;
promise->add(a.get_future());
promise->add(b.get_future());
THEN("waits for sub futures")
{
auto future = promise->get_future();
promise = nullptr;
Timer timer;
std::thread threadA([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
a.set_value(1);
b.set_value("hello");
});
future.wait();
auto elapsed = timer.elapsed();
threadA.join();
REQUIRE(elapsed > 0.1);
}
}
}
}
} // namespace
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,48 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/future/after.hpp>
#include <tjp/core/testing/catch.hpp>
namespace tjp {
namespace core {
SCENARIO("after a future")
{
GIVEN("two promises")
{
Promise<int> p0, p1;
WHEN("a future from one promise")
{
auto f0 = p0.get_future();
WHEN("get a different future after")
{
auto added = after(
f0,
[&](auto &&f)
{
auto r = f.get();
return p1.get_future().then([r](auto &&f) {
return r + f.get();
});
}
);
THEN("values are correct passed through")
{
p0.set_value(1);
REQUIRE(f0.get() == 1);
REQUIRE(added.is_set() == false);
p1.set_value(2);
REQUIRE(added.get() == 3);
}
}
}
}
}
} // namespace
} // namespace

43
tjp/core/future/after.hpp Executable file
View File

@@ -0,0 +1,43 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include "weak_future_consume.hpp"
#include <utility>
namespace tjp {
namespace core {
template<typename F0, typename FutureGenerator>
auto after(F0 &&f0, FutureGenerator &&next)
{
typedef decltype(f0.get()) R0;
typedef decltype(next(f0)) F1_Ref;
typedef typename std::remove_reference<F1_Ref>::type F1;
typedef decltype(std::declval<F1>().get()) R1_Ref;
typedef typename std::remove_reference<R1_Ref>::type R1;
Promise<R1> p;
auto p_ = weak(p.shared);
auto swizzle = f0.then([p_, next](auto &&f0) {
return
future_with([&]() { return next(f0); })
.then([p_](auto &&f1) {
weak_future_consume(
p_,
[](auto &&f1) { return f1.get(); },
f1
);
});
});
return p.get_future().then([swizzle](auto &&f1) {
return f1.get();
});
}
} // namespace
} // namespace

17
tjp/core/future/after_using.hpp Executable file
View File

@@ -0,0 +1,17 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "after.hpp"
namespace tjp::core {
template<typename F0, typename FutureGenerator>
auto after_using(F0 &&f0, FutureGenerator &&next)
{
return after(f0, [next = std::move(next)](auto &&f) {
return next(f.get());
});
}
} // namespace

23
tjp/core/future/boost/Future.h Executable file
View File

@@ -0,0 +1,23 @@
// TJP COPYRIGHT HEADER
#pragma once
#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <functional>
namespace tjp {
namespace core {
template<typename T>
using Future = boost::future<T>;
template<typename T>
using Promise = boost::promise<T>;
} // namespace
} // namespace

View File

@@ -0,0 +1,28 @@
// TJP COPYRIGHT HEADER
#include "Future.hpp"
namespace tjp {
namespace core {
namespace futures {
namespace detail {
//typedef std::list<Cancelable *> Stack;
//ThreadSpecificSingleton<Stack> stack;
//CancelFrame::CancelFrame(Cancelable *c)
//{
// stack->push_back(c);
//}
//CancelFrame::~CancelFrame()
//{
// stack->pop_back();
//}
} // namespace
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,6 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future_.h"
#include "Future_void.h"

View File

@@ -0,0 +1,14 @@
// TJP COPYRIGHT HEADER
#pragma once
//#define FUTURES_ENABLE_LOG
//#define FUTURES_THEN_ON_CONSTRUCT
#define FUTURES_THEN_ON_SET
//#define FUTURES_THEN_ON_WAIT
//#define CORE_FUTURE_USE_STRONG_EXCEPTION_PTR
#define CORE_FUTURE_USE_STD_EXCEPTION_PTR
#include "Future_.hpp"
#include "Future_void.hpp"

View File

@@ -0,0 +1,69 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp {
namespace core {
namespace futures {
namespace detail {
template<typename T>
struct Shared;
template<typename T>
struct Shared_;
template<typename T>
struct Promise;
template<typename T>
struct Future;
struct Dependency;
struct Cancelable;
struct CancelFrame;
template<typename T>
struct Then;
template<typename T>
struct Shared_;
template<typename T>
struct Shared;
template<typename T>
struct Promise_;
template<typename T>
struct Promise;
template<typename T>
struct Future_;
template<typename T>
struct Future;
} // namespace
template<typename T>
using Promise = detail::Promise<T>;
template<typename T>
using Future = detail::Future<T>;
} // namespace
template<typename T>
using Promise = futures::Promise<T>;
template<typename T>
using Future = futures::Future<T>;
} // namespace
} // namespace

View File

@@ -0,0 +1,513 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future_Requirements.hpp"
namespace tjp {
namespace core {
namespace futures {
namespace detail {
template<typename T>
struct Shared;
template<typename T>
struct Shared_;
template<typename T>
struct Promise;
template<typename T>
struct Future;
struct Dependency {
virtual ~Dependency() {}
} ;
#if defined(CORE_FUTURE_USE_STRONG_EXCEPTION_PTR)
using ExceptionPtr = StrongPtr<Exception>;
inline
void future_rethrow_exception(const ExceptionPtr &exception)
{
throw *exception;
}
template<typename T>
ExceptionPtr make_exception(T &&t)
{
return strong<std::decay_t<T>>(std::forward<T>(t));
}
#define CORE_FUTURE_CATCH_BEGIN(e) \
catch (Exception &e)
#define CORE_FUTURE_CATCH_END
#elif defined(CORE_FUTURE_USE_STD_EXCEPTION_PTR)
using ExceptionPtr = std::exception_ptr;
inline
void future_rethrow_exception(const ExceptionPtr &exception)
{
std::rethrow_exception(exception);
}
template<typename T>
ExceptionPtr make_exception(T &&t)
{
try
{
throw t;
}
catch (...)
{
return std::current_exception();
}
}
inline
ExceptionPtr make_exception(ExceptionPtr exception)
{
return exception;
}
#define CORE_FUTURE_CATCH_BEGIN(e) \
catch (...) \
{ \
auto e = std::current_exception(); \
#define CORE_FUTURE_CATCH_END \
}
#endif
template<typename T>
struct Then : Dependency
{
StrongPtr<Shared<T>> dependency;
typedef std::function<void(const StrongPtr<Shared<T>> &)> F;
F f;
Then(const StrongPtr<Shared<T>> &dependency_, F &&f_) :
dependency(dependency_),
f(std::move(f_))
{
FuturesLogDebug(tjp::core::futures::Then, logOfThis(this) << "constructor " << type_id<T>().name());
}
~Then ()
{
FuturesLogDebug(tjp::core::futures::Then, logOfThis(this) << "destructor " << type_id<T>().name());
}
void execute()
{
auto f_ = std::move(f);
f_(dependency);
}
} ;
template<typename T>
struct Shared_
{
typedef Shared_<T> Self;
StrongPtr<Dependency> dependency;
ExceptionPtr exception;
bool isSet;
mutable Event::Mutex mutex;
mutable Event event;
Shared_(const Shared_ &) = delete;
Shared_(Shared_ &&) = delete;
Shared_ () :
isSet(false)
{
FuturesLogDebug(tjp::core::futures::Shared_, logOfThis(this) << "constructor " << type_id<T>().name());
}
~Shared_ ()
{
FuturesLogDebug(tjp::core::futures::Shared_, logOfThis(this) << "destructor " << type_id<T>().name());
}
template<typename E>
void except_hasLock(E &&e)
{
debug_assert(mutex.locked_by_caller());
exception = make_exception(std::forward<E>(e));
set_notify();
}
void set_notify ()
{
FuturesLogDebug(tjp::core::futures::Shared_::set_notify, logOfThis(this) << type_id<T>().name());
debug_assert(!isSet);
isSet = true;
dependency = {};
event.notify_all();
}
void wait () const
{
// first time before lock, fast
auto l = lock_of(mutex);
while (!isSet)
event.wait(l);
#ifdef FUTURES_THEN_ON_WAIT
if (!thens.empty())
const_cast<Self *>(this)->execute(l);
#endif
}
void raise () const
{
wait();
if (exception)
future_rethrow_exception(exception);
}
bool is_set () const
{
return isSet;
}
typedef std::list<WeakPtr<Then<T>>> Thens;
Thens thens;
template<typename R, typename F>
auto generate_then(
const StrongPtr<Shared<T>> &self,
const WeakPtr<Shared<R>> &value,
F &&f
)
{
auto then = strong<Then<T>>(
self,
[value, f=std::move(f)](const StrongPtr<Shared<T>> &t) {
if (auto value_ = strong(value))
{
Promise<R> promise(value_);
promise.consume(f, Future<T>(t));
}
}
);
return then;
}
template<typename R, typename F>
auto then(const StrongPtr<Shared<T>> &self, F &&f)
{
auto value = strong<Shared<R>>();
Future<R> future(value);
auto then__ = generate_then(self, weak(value), std::forward<F>(f));
value->dependency = then__;
thens.push_back(weak(then__));
return future;
}
#ifdef FUTURES_THEN_ON_WAIT
virtual void execute (Event::Mutex::Lock &l) = 0;
#endif
} ;
template<typename T>
struct Shared : Shared_<T>
{
typedef Shared_<T> Super;
Optional<T> value;
template<typename ... R>
void set(R && ... value_)
{
Event::Mutex::Lock l(Super::mutex);
value.emplace(std::forward<R>(value_)...);
Super::set_notify();
execute(l);
}
// void set(const T &value_)
// {
// Event::Mutex::Lock l(Super::mutex);
// value = value_;
// Super::set_notify();
// execute(l);
// }
template<typename E>
void except(E &&e)
{
Event::Mutex::Lock l(Super::mutex);
Super::except_hasLock(std::forward<E>(e));
execute(l);
}
// const T &get () const
// {
// Super::raise();
// return value;
// }
T &get ()
{
Super::raise();
return *value;
}
#ifdef FUTURES_THEN_ON_CONSTRUCT
template<typename F>
auto then(const StrongPtr<Shared<T>> &self, F &&f)
{
using R = decltype(f(Future<T>(this)));
Super::wait();
Promise<R> promise;
promise.consume(f, Future<T>(self));
return promise.get_future();
}
#else
template<typename F>
auto then(const StrongPtr<Shared<T>> &self, F &&f)
{
Event::Mutex::Lock l(Super::mutex);
using R = decltype(f(Future<T>(self)));
auto future = Super::template then<R>(self, std::forward<F>(f));
if (Super::is_set())
execute(l);
return future;
}
#endif
void execute (Event::Mutex::Lock &l)
#ifdef FUTURES_THEN_ON_WAIT
override
#endif
{
FuturesLogDebug(tjp::core::futures::Shared_::execute, logOfThis(this) << type_id<T>().name());
auto thens_ = std::move(Super::thens);
l.unlock();
while (!thens_.empty())
{
auto &then = thens_.front();
if (auto then_ = strong(then))
then_->execute();
thens_.pop_front();
}
}
} ;
template<typename T>
struct Promise_
{
using shared_ptr = StrongPtr<Shared<T>>;
shared_ptr shared;
Promise_() :
shared(strong<Shared<T>>())
{
}
Promise_(const StrongPtr<Shared<T>> &shared_) : shared(shared_) {}
template<typename E>
static void set_exception(shared_ptr &shared, E &&exception)
{
shared->except(std::forward<E>(exception));
}
template<typename E>
void set_exception(E &&exception)
{
set_exception(shared, std::forward<E>(exception));
}
Future<T> get_future ()
{
return Future<T>(shared);
}
bool is_set ()
{
return shared->is_set();
}
} ;
template<typename T>
struct Promise : public Promise_<T>
{
typedef Promise_<T> Super;
using shared_ptr = typename Super::shared_ptr;
Promise() {}
Promise(const StrongPtr<Shared<T>> &value_) : Super(value_) {}
~Promise () {}
template<typename ... R>
static void set_value(shared_ptr &shared, R && ...t)
{
shared->set(std::forward<R>(t)...);
}
// static void set_value(shared_ptr &shared, const T &t)
// {
// shared->set(t);
// }
template<typename ... R>
void set_value(R && ...t)
{
set_value(Super::shared, std::forward<R>(t)...);
}
// void set_value(const T &t)
// {
// set_value(Super::shared, t);
// }
template<typename F, typename ... V>
void consume(const F &f, V && ...v)
{
auto shared_ = weak(Super::shared);
try
{
auto result = f(std::forward<V>(v)...);
if (auto shared = strong(shared_))
set_value(shared, std::forward<decltype(result)>(result));
}
CORE_FUTURE_CATCH_BEGIN(e)
{
if (auto shared = strong(shared_))
Super::set_exception(shared, e);
}
CORE_FUTURE_CATCH_END
}
template<typename F>
void consume(const F &f)
{
auto shared_ = weak(Super::shared);
try
{
auto result = f();
if (auto shared = strong(shared_))
this->set_value(shared, std::forward<decltype(result)>(result));
}
CORE_FUTURE_CATCH_BEGIN(e)
{
if (auto shared = strong(shared_))
Super::set_exception(shared, e);
}
CORE_FUTURE_CATCH_END
}
} ;
template<typename T>
struct Future_
{
typedef T value_type;
StrongPtr<Shared<T>> shared;
Future_(const StrongPtr<Shared<T>> &shared_) : shared(shared_) {}
Future_() {}
~Future_ ()
{
FuturesLogDebug(tjp::core::futures::Future_, logOfThis(this) << " destructing " << type_id<T>().name() << ": " << SmartPtrBase::getInfoFor(ptr_of(shared)));
}
void wait () const
{
shared->wait();
}
bool valid () const
{
return (bool)shared;
}
bool is_set () const
{
return shared->is_set();
}
void cancel ()
{
shared->cancel();
}
} ;
template<typename T>
struct Future : public Future_<T>
{
typedef Future_<T> Super;
typedef T value_type;
Future(const StrongPtr<Shared<T>> &promise_) : Super(promise_) {}
Future() {}
T &get ()
{
return Super::shared->get();
}
void raise() const
{
return Super::shared->raise();
}
template<typename F>
auto then(F &&f)
{
return Super::shared->then(Super::shared, std::forward<F>(f));
}
} ;
} // namespace
template<typename T>
using Promise = detail::Promise<T>;
template<typename T>
using Future = detail::Future<T>;
} // namespace
template<typename T>
using Promise = futures::Promise<T>;
template<typename T>
using Future = futures::Future<T>;
} // namespace
} // namespace

View File

@@ -0,0 +1,24 @@
// TJP COPYRIGHT HEADER
#pragma once
#ifdef TIMPREPSCIUS_CORE_STANDALONE
#include "StandaloneCore.ipp"
#else
#include <tjp/core/threads/Lock.hpp>
#include <tjp/core/exception/Exception.hpp>
#include <tjp/core/assert/debug_assert.h>
#include <tjp/core/ptr/Ptr.hpp>
#include <tjp/core/containers/Optional.hpp>
#endif
#include <queue>
#include <functional>
#include <list>
#ifdef FUTURES_ENABLE_LOG
#include "../../log/Log.h"
#define FuturesLogDebug(x,y) xLogDebug(x,y)
#else
#define FuturesLogDebug(x,y)
#endif

View File

@@ -0,0 +1,31 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future_.h"
namespace tjp {
namespace core {
namespace futures {
namespace detail {
template<>
struct Future<void>;
template<>
struct Promise<void>;
template<>
struct Shared<void>;
template<>
struct Future<void>;
} // namespace
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,195 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future_void.h"
#include "Future_.hpp"
namespace tjp {
namespace core {
namespace futures {
namespace detail {
template<>
struct Future<void>;
template<>
struct Promise<void>;
template<>
struct Shared<void> : Shared_<void>
{
typedef Shared_<void> Super;
void set()
{
Event::Mutex::Lock l(Super::mutex);
Super::set_notify();
execute(l);
}
template<typename E>
void except(E &&e)
{
Event::Mutex::Lock l(Super::mutex);
Super::except_hasLock(std::forward<E>(e));
execute(l);
}
void get ()
{
Super::raise();
}
using Super::raise;
template<typename F>
auto then(const StrongPtr<Shared<void>> &self, F &&f);
void execute (Event::Mutex::Lock &l)
#ifdef FUTURES_THEN_ON_WAIT
override
#endif
{
auto thens_ = std::move(Super::thens);
l.unlock();
while (!thens_.empty())
{
auto &then = thens_.front();
if (auto then_ = strong(then))
then_->execute();
thens_.pop_front();
}
}
} ;
template<>
struct Promise<void> : Promise_<void>
{
typedef Promise_<void> Super;
Promise() {}
Promise(const StrongPtr<Shared<void>> &shared_) : Super(shared_) {}
~Promise() {}
static void set_value(shared_ptr &shared)
{
shared->set();
}
void set_value()
{
set_value(Super::shared);
}
void get ()
{
Super::shared->get();
}
template<typename F, typename ... V>
void consume(const F &f, V && ... v)
{
auto shared_ = weak(Super::shared);
try
{
f(std::forward<V>(v)...);
if (auto shared = strong(shared_))
set_value(shared);
}
CORE_FUTURE_CATCH_BEGIN(e)
{
if (auto shared = strong(shared_))
Super::set_exception(shared, e);
}
CORE_FUTURE_CATCH_END
}
template<typename F>
void consume(const F &f)
{
auto shared_ = weak(Super::shared);
try
{
f();
if (auto shared = strong(shared_))
set_value(shared);
}
CORE_FUTURE_CATCH_BEGIN(e)
{
if (auto shared = strong(shared_))
Super::set_exception(shared, e);
}
CORE_FUTURE_CATCH_END
}
} ;
template<>
struct Future<void> : Future_<void>
{
typedef Future_<void> Super;
Future(const StrongPtr<Shared<void>> &shared_) : Super(shared_) {}
Future() {}
void get ()
{
shared->get();
}
void raise() const
{
shared->raise();
}
template<typename F>
auto then(F &&f)
{
return Super::shared->then(Super::shared, std::forward<F>(f));
}
} ;
//------------
#ifdef FUTURES_THEN_ON_CONSTRUCT
template<typename F>
auto Shared<void>::then(const StrongPtr<Shared<void>> &self, F &&f)
{
using R = decltype(f(Future<void>(this)));
Super::wait();
Promise<R> promise;
promise.consume(f, Future<void>(self));
return promise.get_future();
}
#else
template<typename F>
auto Shared<void>::then(const StrongPtr<Shared<void>> &self, F &&f)
{
Event::Mutex::Lock l(Super::mutex);
using R = decltype(f(Future<void>(StrongPtr<Shared<void>>())));
auto future = Super::template then<R>(self, std::forward<F>(f));
if (Super::is_set())
execute(l);
return future;
}
#endif
} // namespace
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,394 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/future/custom/Future.hpp>
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/types/Types.h>
#include <tjp/core/log/Log.h>
#include <tjp/core/log/LogOf.h>
namespace tjp {
namespace core {
namespace futures {
struct StructX {
char bytes[32];
} ;
struct CustomExceptionNotHandled {};
struct CustomExceptionHandled: Exception {
int x;
CustomExceptionHandled(const String &s, int x_) :
Exception(s),
x(x_)
{
}
} ;
SCENARIO("futures with thens", "[core::future]" )
{
GIVEN("sizeof")
{
Promise<void> ap;
Promise<u8> bp;
Promise<u32> cp;
Promise<u64> dp;
Promise<StructX> ep;
sLogTest("testing", logVar(sizeof(ap)) << logVar(sizeof(bp)) << logVar(sizeof(cp)) << logVar(sizeof(dp)) << logVar(sizeof(ep)));
Future<void> af;
Future<u8> bf;
Future<u32> cf;
Future<u64> df;
Future<StructX> ef;
sLogTest("testing", logVar(sizeof(af)) << logVar(sizeof(bf)) << logVar(sizeof(cf)) << logVar(sizeof(df)) << logVar(sizeof(ef)));
af = ap.get_future();
bf = bp.get_future();
cf = cp.get_future();
df = dp.get_future();
ef = ep.get_future();
sLogTest("testing", logVar(sizeof(ap)) << logVar(sizeof(bp)) << logVar(sizeof(cp)) << logVar(sizeof(dp)) << logVar(sizeof(ep)));
sLogTest("testing", logVar(sizeof(af)) << logVar(sizeof(bf)) << logVar(sizeof(cf)) << logVar(sizeof(df)) << logVar(sizeof(ef)));
ap.set_value();
bp.set_value(0);
cp.set_value(0);
dp.set_value(0);
ep.set_value(StructX{});
sLogTest("testing", logVar(sizeof(ap)) << logVar(sizeof(bp)) << logVar(sizeof(cp)) << logVar(sizeof(dp)) << logVar(sizeof(ep)));
sLogTest("testing", logVar(sizeof(af)) << logVar(sizeof(bf)) << logVar(sizeof(cf)) << logVar(sizeof(df)) << logVar(sizeof(ef)));
}
GIVEN("a void promise")
{
Promise<void> p;
WHEN("setting a value directly")
{
auto future = p.get_future();
p.set_value();
future.wait();
REQUIRE(true);
}
WHEN ("manifesting an exception")
{
auto future = p.get_future()
.then([](auto &&f) {
throw Exception("made exception");
});
p.set_value();
try
{
future.get();
REQUIRE(false);
}
catch (Exception &e)
{
REQUIRE(e.what() == "made exception");
}
}
WHEN("manifesting a custom handled exception in then")
{
auto future = p.get_future()
.then([](auto &&f) {
throw CustomExceptionHandled("made exception", 42);
});
p.set_value();
try
{
future.get();
REQUIRE(false);
}
catch (CustomExceptionHandled &e)
{
REQUIRE(e.what() == "made exception");
REQUIRE(e.x == 42);
}
catch (Exception &e)
{
REQUIRE(false);
}
catch (...)
{
REQUIRE(false);
}
}
WHEN("manifesting a custom handled exception")
{
p.consume([]() {
throw CustomExceptionHandled("made exception", 42);
});
auto future = p.get_future();
try
{
future.get();
REQUIRE(false);
}
catch (CustomExceptionHandled &e)
{
REQUIRE(e.what() == "made exception");
REQUIRE(e.x == 42);
}
catch (Exception &e)
{
REQUIRE(false);
}
}
WHEN ("manifesting an exception through a chain")
{
auto future = p.get_future()
.then([](auto &&f) {
throw Exception("made exception");
return 1;
})
.then([](const auto &f) {
return -1.0;
});
p.set_value();
auto value = future.get();
REQUIRE(value == -1);
}
}
GIVEN("a void promise ptr")
{
Promise<void> *p;
p = new Promise<void>();
WHEN("setting a consume")
{
auto future = p->get_future();
p->consume([&](){ delete p; p = nullptr; });
future.wait();
REQUIRE(true);
}
}
GIVEN("an integer promise")
{
Promise<int> p;
WHEN("setting a value directly")
{
auto future = p.get_future();
p.set_value(1);
future.wait();
auto value = future.get();
REQUIRE(value == 1);
}
WHEN ("settings a value with a then")
{
auto future = p.get_future();
auto next = future.then([](auto f) {
return f.get() + 1;
});
p.set_value(1);
auto value = next.get();
REQUIRE(value == 2);
}
WHEN ("settings a value with a then chain")
{
auto future = p.get_future()
.then([](auto &&f) {
return f.get() + 1;
})
.then([](auto &&f) {
return f.get() + 1;
});
p.set_value(1);
auto value = future.get();
REQUIRE(value == 3);
}
WHEN ("settings a value with a then chain changing type")
{
auto future = p.get_future()
.then([](auto &&f) {
return f.get() + 1;
})
.then([](auto &&f) {
return f.get() + 1.5;
});
p.set_value(1);
auto value = future.get();
REQUIRE(value == 3.5);
}
WHEN ("manifesting an exception")
{
auto future = p.get_future()
.then([](auto &&f) {
throw Exception("made exception");
return f.get()+1;
})
.then([](auto &&f) {
return f.get() + 1.5;
});
p.set_value(1);
try
{
future.get();
REQUIRE(false);
}
catch (Exception &e)
{
REQUIRE(e.what() == "made exception");
}
}
WHEN ("manifesting an exception but ignoring")
{
auto future = p.get_future()
.then([](auto &&f) {
throw Exception("made exception");
return f.get()+1;
})
.then([](auto &&f) {
try
{
return f.get() + 1.5;
}
catch (Exception &e)
{
return -1.0;
}
});
p.set_value(1);
auto value = future.get();
REQUIRE(value == -1);
}
WHEN ("manifesting an exception but not even checking")
{
auto future = p.get_future()
.then([](auto &&f) {
throw Exception("made exception");
return f.get()+1;
})
.then([](auto &&f) {
return -1.0;
});
p.set_value(1);
auto value = future.get();
REQUIRE(value == -1);
}
}
GIVEN("an strongptr promise")
{
Promise<StrongPtr<int>> p;
WHEN ("settings a value with a then chain changing type")
{
auto future = p.get_future()
.then([](auto &&f) {
return *f.get() + 1;
})
.then([](auto &&f) {
return f.get() + 1;
});
p.set_value(strong<int>(1));
auto value = future.get();
REQUIRE(value == 3);
}
}
GIVEN("a promise")
{
Promise<int> p;
WHEN ("a future with multiple thens")
{
auto future = p.get_future();
auto then1 = future.then([](auto &&f) {
return f.get() + 1;
});
auto then2 = future.then([](auto &&f) {
return f.get() + 2;
});
p.set_value(1);
auto value1 = then1.get();
auto value2 = then2.get();
auto value = value1 + value2;
REQUIRE(value == 5);
}
WHEN ("a future with multiple thens")
{
auto future = p.get_future();
Future<int> thena, thenb;
{
auto then1 = future.then([](auto &&f) {
return f.get() + 1;
});
auto then2 = future.then([](auto &&f) {
return f.get() + 2;
});
thena = then1.then([](auto &&f) {
return f.get() + 1;
});
thenb = then2.then([](auto &&f) {
return f.get() + 1;
});
}
p.set_value(1);
auto value1 = thena.get();
auto value2 = thenb.get();
auto value = value1 + value2;
REQUIRE(value == 7);
}
}
}
} // namespace
} // namespace
} // namespace

19
tjp/core/future/folly/Future.h Executable file
View File

@@ -0,0 +1,19 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <folly/futures/Future.h>
namespace tjp {
namespace core {
template<typename T>
using Promise = folly:Promise<T>;
template<typename T>
using Future = folly::Future<T>;
} // namespace
} // namespace

View File

@@ -0,0 +1,30 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include <type_traits>
namespace tjp::core {
// Non-void version
template<typename T,
typename R = std::decay_t<decltype(std::declval<T>().get())>
// typename = std::enable_if_t<!std::is_void_v<R>>
>
Optional<R> future_discard_exception(T &&f)
{
try
{
return f.get();
}
CORE_FUTURE_CATCH_BEGIN(e)
{
}
CORE_FUTURE_CATCH_END
return {};
}
} // namespace

View File

@@ -0,0 +1,27 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
namespace tjp {
namespace core {
template<typename T, typename U>
Future<T> future_handle_exception(Future<T> &&f, U &&u)
{
return f.then([u_ = std::move(u)](auto &&f) {
try
{
return f.get();
}
catch (Exception &e)
{
u_(e);
throw e;
}
});
}
} // namespace
} // namespace

View File

@@ -0,0 +1,25 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
namespace tjp {
namespace core {
template<typename T>
bool future_is_exception(Future<T> &f)
{
try
{
f.get();
return false;
}
catch (...)
{
return true;
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,29 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include <type_traits>
namespace tjp::core {
template<typename T, typename U,
typename R = decltype(std::declval<T>().get())
// typename = std::enable_if_t<!std::is_void_v<R>>
>
R future_on_exception(T &&f, U &&u)
{
try
{
return f.get();
}
CORE_FUTURE_CATCH_BEGIN(e)
{
u(e);
throw;
}
CORE_FUTURE_CATCH_END
}
} // namespace

View File

@@ -0,0 +1,39 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include <type_traits>
namespace tjp::core {
template<typename T, typename U,
typename R = decltype(std::declval<T>().get())
// typename = std::enable_if_t<!std::is_void_v<R>>
>
R future_on_exception_value(T &&f, U &&u)
{
try
{
return f.get();
}
catch(std::exception &e)
{
u(Exception(e.what()));
throw;
}
catch(Exception &e)
{
u(e);
throw;
}
CORE_FUTURE_CATCH_BEGIN(e)
{
u(Exception("Unknown"));
throw;
}
CORE_FUTURE_CATCH_END
}
} // namespace

View File

@@ -0,0 +1,39 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include <type_traits>
namespace tjp::core {
template<typename T, typename U,
typename R = decltype(std::declval<T>().get())
// typename = std::enable_if_t<!std::is_void_v<R>>
>
R future_on_exception_what(T &&f, U &&u)
{
try
{
return f.get();
}
catch(std::exception &e)
{
u(String(e.what()));
throw;
}
catch(Exception &e)
{
u(e.what());
throw;
}
CORE_FUTURE_CATCH_BEGIN(e)
{
u(String("Unknown"));
throw;
}
CORE_FUTURE_CATCH_END
}
} // namespace

View File

@@ -0,0 +1,29 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Future.hpp"
#include <tjp/core/containers/Optional.hpp>
namespace tjp {
namespace core {
template<typename F>
auto future_success(F &&f)
{
typedef typename std::remove_reference<decltype(f.get())>::type R;
try
{
if (f.valid())
return Optional<R> { f.get() };
}
catch(...)
{
}
return Optional<R>{};
}
} // namespace
} // namespace

19
tjp/core/future/liblw/Future.h Executable file
View File

@@ -0,0 +1,19 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <lw/event/Promise.impl.hpp>
namespace tjp {
namespace core {
template<typename T>
using Promise = lw::event::Promise<T>;
template<typename T>
using Future = lw::event::Future<T>;
} // namespace
} // namespace

View File

@@ -0,0 +1,57 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <type_traits>
namespace tjp::core {
template<typename T, typename F, typename ... V>
void weak_future_consume_value(T &&shared_, F &&f, V && ... v)
{
try
{
auto result = std::forward<F>(f)(std::forward<V>(v)...);
if (auto shared = strong(shared_))
shared->set(std::move(result));
}
CORE_FUTURE_CATCH_BEGIN(e)
{
if (auto shared = strong(shared_))
shared->except(std::move(e));
}
CORE_FUTURE_CATCH_END
}
template<typename T, typename F, typename ... V>
void weak_future_consume_void(T &&shared_, F &&f, V && ... v)
{
try
{
std::forward<F>(f)(std::forward<V>(v)...);
if (auto shared = strong(shared_))
shared->set();
}
CORE_FUTURE_CATCH_BEGIN(e)
{
if (auto shared = strong(shared_))
shared->except(std::move(e));
}
CORE_FUTURE_CATCH_END
}
template<typename T, typename F, typename ... V>
void weak_future_consume(T &&shared_, F &&f, V && ... v)
{
using R_Ref = decltype(std::forward<F>(f)(std::forward<V>(v)...));
using R = std::remove_reference_t<R_Ref>();
if constexpr(std::is_void_v<R_Ref>)
return weak_future_consume_void(std::forward<T>(shared_), std::forward<F>(f), std::forward<V>(v)...);
else
return weak_future_consume_value(std::forward<T>(shared_), std::forward<F>(f), std::forward<V>(v)...);
}
} // namespace