AuroraRuntime/Include/Aurora/Async/IPCPromises.hpp

722 lines
21 KiB
C++
Raw Normal View History

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: IPCPromises.hpp
Date: 2022-07-19
Author: Reece
***/
#pragma once
struct AuNullS
{
AuUInt8 null;
};
namespace Aurora::Async
{
struct IPromiseCompleter
{
virtual void Complete() = 0;
virtual void Fail() = 0;
};
struct IPromiseComplete
{
virtual void OnSuccess(void *in) = 0;
};
struct IPromiseFailure
{
virtual void OnFailure(void *in) = 0;
};
struct IPromisePtrs
{
virtual void *GetSuccessPtr() = 0;
virtual void *GetErrorPtr() = 0;
virtual AuSPtr<void> GetData() = 0;
};
template <class SuccessValue_t>
struct PromiseSuccessTmpl : IPromiseComplete
{
using OnSuccess_f = AuFunction<void(const AuSPtr<SuccessValue_t> &)>;
inline virtual void OnSuccess(void *in);
inline void Init(IPromisePtrs *parent, const OnSuccess_f &onDelegate) noexcept
{
this->parent_ = parent;
this->onSuccess_ = onDelegate;
}
private:
IPromisePtrs *parent_;
OnSuccess_f onSuccess_;
};
template <class SuccessValue_t, class ErrorValue_t = AuNullS>
struct PromiseCallback : IPromiseFailure, IPromiseComplete
{
inline PromiseCallback()
{}
inline virtual void OnFailure(void *in) override
{
this->OnFailure(AuReinterpretCast<ErrorValue_t *>(in));
}
inline virtual void OnSuccess(void *in) override
{
this->OnSuccess(AuReinterpretCast<SuccessValue_t *>(in));
}
virtual void OnFailure(ErrorValue_t *in) = 0;
virtual void OnSuccess(SuccessValue_t *in) = 0;
};
template <class SuccessValue_t, class ErrorValue_t = AuNullS>
struct PromiseCallbackFunctional : PromiseCallback<SuccessValue_t, ErrorValue_t>, AuEnableSharedFromThis<PromiseCallbackFunctional<SuccessValue_t, ErrorValue_t>>
{
using OnSuccess_f = AuFunction<void(const AuSPtr<SuccessValue_t> &)>;
using OnFailure_f = AuFunction<void(const AuSPtr<ErrorValue_t> &)>;
inline PromiseCallbackFunctional(OnSuccess_f onSuccess,
OnFailure_f onFailure) :
onSuccess(onSuccess),
onFailure(onFailure)
{
}
inline PromiseCallbackFunctional(OnSuccess_f onSuccess) :
onSuccess(onSuccess)
{
}
inline PromiseCallbackFunctional()
{
}
virtual void OnFailure(ErrorValue_t *in) override
{
if (this->onFailure)
{
if (!in)
{
this->onFailure({});
}
else
{
this->onFailure(AuSPtr<ErrorValue_t>(AuSharedFromThis(), in));
}
}
}
virtual void OnSuccess(SuccessValue_t *in) override
{
if (this->onSuccess)
{
if (!in)
{
this->onSuccess({});
}
else
{
this->onSuccess(AuSPtr<SuccessValue_t>(AuSharedFromThis(), in));
}
}
}
OnFailure_f onFailure;
OnSuccess_f onSuccess;
};
template <class ErrorValue_t>
struct PromiseFailTmpl : IPromiseFailure
{
using OnFailure_f = AuFunction<void(const AuSPtr<ErrorValue_t> &)>;
inline virtual void OnFailure(void *in) override;
inline void Init(IPromisePtrs *parent, const OnFailure_f &onDelegate) noexcept
{
this->parent_ = parent;
this->onFailure_ = onDelegate;
}
private:
IPromisePtrs *parent_;
OnFailure_f onFailure_;
};
template <class SuccessValue_t, class ErrorValue_t>
struct Promise;
template <class SuccessValue_t, class ErrorValue_t>
struct IPromiseAccessor : IPromiseCompleter
{
inline virtual AuSPtr<SuccessValue_t> WriteIntoSuccess() = 0;
inline virtual AuSPtr<ErrorValue_t> WriteIntoError() = 0;
};
struct IPromiseDelegate
{
virtual void OnPromiseDelegate(const AuSPtr<IPromiseCompleter> &promise) = 0;
};
template <class SuccessValue_t, class ErrorValue_t>
struct PromiseCallTmpl : IPromiseDelegate
{
using OnDelegate_f = AuFunction<void(const AuSPtr<IPromiseAccessor<SuccessValue_t, ErrorValue_t>> &)>;
inline virtual void OnPromiseDelegate(const AuSPtr<IPromiseCompleter> &promise) override;
inline void Init(IPromisePtrs *parent, const OnDelegate_f &onDelegate) noexcept
{
this->parent_ = parent;
this->onDelegate_ = onDelegate;
}
private:
IPromisePtrs *parent_;
OnDelegate_f onDelegate_;
};
struct IPromisePrivateCallbacks
{
virtual void OnCBTick() = 0;
virtual void OnCBDelegate() = 0;
virtual void OnCBFailure() = 0;
};
struct PromiseStartupAdapter : IWorkItemHandler
{
inline PromiseStartupAdapter(IPromisePrivateCallbacks *parent) noexcept
{
this->parent_ = parent;
}
inline virtual void DispatchFrame(IWorkItemHandler::ProcessInfo &info) override
{
this->parent_->OnCBDelegate();
};
inline virtual void OnFailure() override
{
SysPushErrorGeneric("Didnt fire initial delegate");
this->parent_->OnCBFailure();
};
private:
IPromisePrivateCallbacks *parent_;
};
struct PromiseTickAdapter : IWorkItemHandler
{
inline PromiseTickAdapter(IPromisePrivateCallbacks *parent) noexcept
{
this->parent_ = parent;
}
inline virtual void DispatchFrame(IWorkItemHandler::ProcessInfo &info) override
{
this->parent_->OnCBTick();
};
inline virtual void OnFailure() override
{
SysPushErrorGeneric("Didnt fire initial delegate");
this->parent_->OnCBFailure();
};
private:
IPromisePrivateCallbacks *parent_;
};
template <class SuccessValue_t, class ErrorValue_t>
struct Promise final : IPromiseAccessor<SuccessValue_t, ErrorValue_t>, AuEnableSharedFromThis<Promise<SuccessValue_t, ErrorValue_t>>, private IPromisePtrs, private IPromisePrivateCallbacks
{
using This_t = Promise<SuccessValue_t, ErrorValue_t>;
AU_SAFE_DESTROY(Promise);
Promise(bool inlineResponse = false, bool allowSync = false);
using FunctionalCall_t = PromiseCallTmpl<SuccessValue_t, ErrorValue_t>;
using FunctionalResponse_t = PromiseSuccessTmpl<SuccessValue_t>;
using FunctionalFail_t = PromiseFailTmpl<ErrorValue_t>;
Further Linux support [+] Begin work on IO futexes for io release on process/thread exit [+] Linux ::readdir iteration [+] AuConsole buffering API [*] Fix sleep as to not get interrupted by signals [*] Switch the type of FS lock used under Linux [*] Linux: Use new IPCHandle encoding scheme [*] Fix undefined behaviour: unintialized timeout values (AuLoop/Linux) [*] Fix undefined behaviour: ConsoleTTY clear line was called of a color of a random value on stack [-] Remainings of std dir iterator [*] Fix pthread_kill (aka send signal to pthread handle) always kills process. This is what you expect bc signal handler inheritance. [*] Reformat the build Aurora.json file [+] Added clang warning ignores to the build file [*] Fix: UNIX need to use STDOUT_FILENO. Was using CRT handle in place of fd by mistake. [+] Linux implementation for IO yield (AuIO::IOYield() - UNIX::LinuxOverlappedYield()) [*] Fix: Linux async end of stream processing. res 0 = zero bytes consumed. <= was detecting this as an error of code 0. Should succeed with zero bytes. [+] Linux LoopQueue missing epilogue hook for the IO processor [*] Various refactors and minor bug fixes [*] Linux fix: Handle pipe EOS as zero [*] Linux fix: thread termination via a user signal of 77. Need a force terminate. [*] IPC handle: fix improper int to bool cast in the header setup within ToString [*] Linux fix: HWInfo CPU topology regression [-] Linux fix: remove SIGABRT handler [*] Missing override in compression, exit, and consoletty headers. [+] Unix Syslog logger backend
2022-08-02 04:52:17 +00:00
using FunctionalResponse_f = typename FunctionalResponse_t::OnSuccess_f;
using FunctionalFail_f = typename FunctionalFail_t::OnFailure_f;
using FunctionalDelegate_f = typename FunctionalCall_t::OnDelegate_f;
inline AuSPtr<This_t> SetCallback(const AuSPtr<PromiseCallback<SuccessValue_t, ErrorValue_t>> &callbacks);
inline AuSPtr<This_t> OnSuccessFunctional(const FunctionalResponse_f &in);
inline AuSPtr<This_t> OnSuccessCallback(const AuSPtr<IPromiseComplete> &in);
inline AuSPtr<This_t> OnFailureFunctional(const FunctionalFail_f &in);
inline AuSPtr<This_t> OnFailureCallback(const AuSPtr<IPromiseFailure> &in);
inline AuSPtr<SuccessValue_t> WriteIntoSuccess() override;
inline AuSPtr<ErrorValue_t> WriteIntoError() override;
inline void Complete() override;
inline void Fail() override;
//inline AuSPtr<IWorkItem> GetBarrier() = 0;
inline bool WaitBeforeWork(const AuSPtr<IWorkItem> &startingBarrier);
inline bool BeginWork(WorkerPId_t worker = GetCurrentWorkerPId());
inline bool BeginWorkEx(const AuSPtr<IPromiseDelegate> &promise, WorkerPId_t worker = GetCurrentWorkerPId());
inline bool BeginWorkFunc(const FunctionalDelegate_f &promise, WorkerPId_t worker = GetCurrentWorkerPId());
inline bool FinalizeWithWork(const AuSPtr<IWorkItem> &sendingFires);
private:
bool bHasBeganWork {};
AuSPtr<IWorkItem> startingBarrier_;
PromiseStartupAdapter startupAdapter;
PromiseTickAdapter tickAdapter;
inline virtual void OnCBTick() override;
inline virtual void OnCBDelegate() override;
inline virtual void OnCBFailure() override;
inline void DestroyPrivate();
inline void *GetSuccessPtr() override;
inline void *GetErrorPtr() override;
inline AuSPtr<void> GetData() override;
void DoStartBlocking();
void DoFailureBlocking();
void DoSuccessBlocking();
void DoEndBlocking();
2022-07-21 22:23:16 +00:00
void DispatchNotifier();
WorkerPId_t currentThread_;
SuccessValue_t success;
ErrorValue_t error;
bool bHasSuccess_ {};
bool bHasError_ {};
bool bHasFiredSuccess_ {};
bool bHasFiredError_ {};
bool bInlineResponse_ {};
FunctionalResponse_t responseFunctional_;
AuSPtr<IPromiseComplete> responseCallback_;
FunctionalFail_t failureFunctional_;
AuSPtr<IPromiseFailure> failureCallback_;
FunctionalCall_t delegateFunctional_;
AuSPtr<IPromiseDelegate> delegateCallback_;
bool bHasSentError_ {};
bool bHasSentSuccess_ {};
Aurora::Threading::Primitives::SpinLock lock_;
AuSPtr<IWorkItem> notifier_;
AuList<AuSPtr<IWorkItem>> startBarriers_;
};
template<class SuccessValue_t, class ErrorValue_t>
inline Promise<SuccessValue_t, ErrorValue_t>::Promise(bool inlineResponse, bool allowSync) : startupAdapter(this), tickAdapter(this)
{
this->bInlineResponse_ = inlineResponse;
auto pid = GetCurrentWorkerPId();
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::DestroyPrivate()
{
if (this->notifier_)
{
this->notifier_->Cancel();
}
this->notifier_.reset();
if (!this->bHasFiredSuccess_ &&
!this->bHasFiredError_)
{
this->bHasFiredError_ = true;
DoFailureBlocking();
}
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::DoStartBlocking()
{}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::DoEndBlocking()
{
this->failureCallback_.reset();
this->responseCallback_.reset();
this->currentThread_ = {};
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::OnCBTick()
{
if (AuExchange(this->bHasFiredError_, false))
{
DoFailureBlocking();
}
else if (AuExchange(this->bHasFiredSuccess_, false))
{
DoSuccessBlocking();
}
else
{
SysPanic("");
}
this->bHasSentSuccess_ = true;
this->bHasSentError_ = true;
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::OnCBDelegate()
{
//SysAssert(this->currentThread_ == GetCurrentWorkerPId());
SysAssert(this->delegateCallback_);
AuExchange(this->delegateCallback_, decltype(this->delegateCallback_) {})->OnPromiseDelegate(AuSharedFromThis());
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::OnCBFailure()
{
Destroy();
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::DoFailureBlocking()
{
{
AU_LOCK_GUARD(this->lock_);
if (AuExchange(this->bHasSentError_, true))
{
return;
}
}
this->DoStartBlocking();
if (this->failureCallback_)
{
this->failureCallback_->OnFailure(this->bHasError_ ? &this->error : nullptr);
}
this->DoEndBlocking();
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::DoSuccessBlocking()
{
{
AU_LOCK_GUARD(this->lock_);
if (AuExchange(this->bHasSentError_, true))
{
return;
}
if (AuExchange(this->bHasSentSuccess_, true))
{
return;
}
}
this->DoStartBlocking();
if (this->responseCallback_)
{
this->responseCallback_->OnSuccess(this->bHasSuccess_ ? &this->success : nullptr);
}
this->DoEndBlocking();
}
template<class SuccessValue_t, class ErrorValue_t>
inline void *Promise<SuccessValue_t, ErrorValue_t>::GetSuccessPtr()
{
return &this->success;
}
template<class SuccessValue_t, class ErrorValue_t>
inline void *Promise<SuccessValue_t, ErrorValue_t>::GetErrorPtr()
{
return &this->error;
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<void> Promise<SuccessValue_t, ErrorValue_t>::GetData()
{
return AuSPtr<void>(AuSharedFromThis());
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::Complete()
{
SysAssert(!this->bHasFiredSuccess_);
SysAssert(!this->bHasFiredError_);
this->bHasFiredSuccess_ = true;
if (this->bInlineResponse_ &&
this->currentThread_ == GetCurrentWorkerPId())
{
this->DoSuccessBlocking();
}
else
{
2022-07-21 22:23:16 +00:00
DispatchNotifier();
}
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::Fail()
{
SysAssert(!this->bHasFiredSuccess_);
this->bHasFiredError_ = true;
if (this->bInlineResponse_ &&
this->currentThread_ == GetCurrentWorkerPId())
{
this->DoFailureBlocking();
}
else
{
2022-07-21 22:23:16 +00:00
DispatchNotifier();
}
}
template<class SuccessValue_t, class ErrorValue_t>
inline void Promise<SuccessValue_t, ErrorValue_t>::DispatchNotifier()
{
if (!this->notifier_)
{
this->notifier_ = this->currentThread_.GetPool()->NewWorkItem(this->currentThread_, AuSPtr<IWorkItemHandler>(AuSharedFromThis(), &this->tickAdapter));
}
2022-07-21 22:23:16 +00:00
SysAssert(this->notifier_);
this->notifier_->Dispatch();
}
template<class SuccessValue_t, class ErrorValue_t>
inline bool Promise<SuccessValue_t, ErrorValue_t>::WaitBeforeWork(const AuSPtr<IWorkItem> &startingBarrier)
{
if (!this->startingBarrier_)
{
this->startingBarrier_ = GetCurrentWorkerPId().GetPool()->NewFence();
}
if (!this->startingBarrier_)
{
return false;
}
this->startingBarrier_->WaitFor(startingBarrier);
return true;
}
template<class SuccessValue_t, class ErrorValue_t>
inline bool Promise<SuccessValue_t, ErrorValue_t>::BeginWork(WorkerPId_t worker)
{
this->currentThread_ = GetCurrentWorkerPId();
SysAssert(worker);
if (worker == GetCurrentWorkerPId())
{
if (this->startingBarrier_)
{
this->startingBarrier_->Dispatch();
return true;
}
this->OnCBDelegate();
return true;
}
else
{
auto newItem = worker.GetPool()->NewWorkItem(worker,
AuSPtr<IWorkItemHandler>(AuSharedFromThis(), &this->startupAdapter));
if (!newItem)
{
return {};
}
if (this->startingBarrier_)
{
this->startingBarrier_->Then(newItem);
}
else
{
newItem->Dispatch();
}
return true;
}
}
template<class SuccessValue_t, class ErrorValue_t>
inline bool Promise<SuccessValue_t, ErrorValue_t>::BeginWorkEx(const AuSPtr<IPromiseDelegate> &promise, WorkerPId_t worker)
{
this->delegateCallback_ = promise;
return BeginWork(worker);
}
template<class SuccessValue_t, class ErrorValue_t>
inline bool Promise<SuccessValue_t, ErrorValue_t>::BeginWorkFunc(const FunctionalDelegate_f &promise, WorkerPId_t worker)
{
this->delegateFunctional_.Init(this, promise);
return BeginWorkEx(AuUnsafeRaiiToShared(&this->delegateFunctional_), worker);
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<Promise<SuccessValue_t, ErrorValue_t>> Promise<SuccessValue_t, ErrorValue_t>::SetCallback(const AuSPtr<PromiseCallback<SuccessValue_t, ErrorValue_t>> &callbacks)
{
this->OnSuccessCallback(callbacks);
this->OnFailureCallback(callbacks);
return AuSPtr<Promise>(AuSharedFromThis());
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<Promise<SuccessValue_t, ErrorValue_t>> Promise<SuccessValue_t, ErrorValue_t>::OnSuccessFunctional(const FunctionalResponse_f &in)
{
this->responseFunctional_.Init(this, in);
return OnSuccessCallback(AuUnsafeRaiiToShared(&this->responseFunctional_));
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<Promise<SuccessValue_t, ErrorValue_t>> Promise<SuccessValue_t, ErrorValue_t>::OnSuccessCallback(const AuSPtr<IPromiseComplete> &in)
{
bool succeedFast {};
{
AU_LOCK_GUARD(this->lock_);
if (this->bHasSentSuccess_)
{
succeedFast = true;
}
else
{
this->responseCallback_ = in;
}
}
if (succeedFast)
{
in->OnSuccess(this->bHasSuccess_ ? &this->success : nullptr);
}
return AuSPtr<Promise>(AuSharedFromThis());
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<Promise<SuccessValue_t, ErrorValue_t>> Promise<SuccessValue_t, ErrorValue_t>::OnFailureFunctional(const FunctionalFail_f &in)
{
this->failureFunctional_.Init(this, in);
return OnFailureCallback(AuUnsafeRaiiToShared(&this->failureFunctional_));
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<Promise<SuccessValue_t, ErrorValue_t>> Promise<SuccessValue_t, ErrorValue_t>::OnFailureCallback(const AuSPtr<IPromiseFailure> &in)
{
bool failFast {};
{
AU_LOCK_GUARD(this->lock_);
if (this->bHasSentError_)
{
failFast = true;
}
else
{
this->failureCallback_ = in;
}
}
if (failFast)
{
2022-07-21 22:23:16 +00:00
in->OnFailure(this->bHasError_ ? &this->error : nullptr);
}
return AuSPtr<Promise>(AuSharedFromThis());
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<SuccessValue_t> Promise<SuccessValue_t, ErrorValue_t>::WriteIntoSuccess()
{
this->bHasSuccess_ = true;
return AuSPtr<SuccessValue_t>(AuSharedFromThis(), &this->success);
}
template<class SuccessValue_t, class ErrorValue_t>
inline AuSPtr<ErrorValue_t> Promise<SuccessValue_t, ErrorValue_t>::WriteIntoError()
{
this->bHasError_ = true;
return AuSPtr<ErrorValue_t>(AuSharedFromThis(), &this->error);
}
template<class SuccessValue_t, class ErrorValue_t>
inline void PromiseCallTmpl<SuccessValue_t, ErrorValue_t>::OnPromiseDelegate(const AuSPtr<IPromiseCompleter> &promise)
{
auto ptr = static_cast<IPromiseAccessor<SuccessValue_t, ErrorValue_t> *>(promise.get());
auto sptr = AuSPtr<IPromiseAccessor<SuccessValue_t, ErrorValue_t>>(promise, ptr);
SysAssert(this->onDelegate_);
this->onDelegate_(sptr);
}
template<class ErrorValue_t>
inline void PromiseFailTmpl<ErrorValue_t>::OnFailure(void *in)
{
SysAssert(this->onFailure_);
if (!in)
{
this->onFailure_({});
}
else
{
this->onFailure_(AuSPtr<ErrorValue_t>(this->parent_->GetData(), (ErrorValue_t *) in));
}
}
template<class SuccessValue_t>
inline void PromiseSuccessTmpl<SuccessValue_t>::OnSuccess(void *in)
{
SysAssert(this->onSuccess_);
if (!in)
{
this->onSuccess_({});
}
else
{
this->onSuccess_(AuSPtr<SuccessValue_t>(this->parent_->GetData(), (SuccessValue_t *)in));
}
}
}
using AuNullPromise = Aurora::Async::Promise<AuNullS, AuNullS>;
template <class Out_t, class Error_t = AuNullS>
using AuPromise = Aurora::Async::Promise<Out_t, Error_t>;