// Copyright Sebastian Jeckel 2014. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef REACT_DOMAIN_H_INCLUDED #define REACT_DOMAIN_H_INCLUDED #pragma once #include "react/detail/Defs.h" #include #include #include "react/detail/DomainBase.h" #include "react/detail/ReactiveInput.h" #include "react/detail/graph/ContinuationNodes.h" #ifdef REACT_ENABLE_LOGGING #include "react/logging/EventLog.h" #include "react/logging/EventRecords.h" #endif //REACT_ENABLE_LOGGING /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// template class Signal; template class VarSignal; template class TempSignal; template class Events; template class EventSource; template class TempEvents; enum class Token; template class Observer; template class ScopedObserver; template class Reactor; template class SignalPack; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Common types & constants /////////////////////////////////////////////////////////////////////////////////////////////////// using REACT_IMPL::TransactionFlagsT; // ETransactionFlags using REACT_IMPL::ETransactionFlags; using REACT_IMPL::allow_merging; #ifdef REACT_ENABLE_LOGGING using REACT_IMPL::EventLog; #endif //REACT_ENABLE_LOGGING // Domain modes using REACT_IMPL::EDomainMode; using REACT_IMPL::sequential; using REACT_IMPL::sequential_concurrent; using REACT_IMPL::parallel; using REACT_IMPL::parallel_concurrent; // Expose enum type so aliases for engines can be declared, but don't // expose the actual enum values as they are reserved for internal use. using REACT_IMPL::EPropagationMode; using REACT_IMPL::WeightHint; /////////////////////////////////////////////////////////////////////////////////////////////////// /// TransactionStatus /////////////////////////////////////////////////////////////////////////////////////////////////// class TransactionStatus { using StateT = REACT_IMPL::SharedWaitingState; using PtrT = REACT_IMPL::WaitingStatePtrT; public: // Default ctor inline TransactionStatus() : statePtr_( StateT::Create() ) {} // Move ctor inline TransactionStatus(TransactionStatus&& other) : statePtr_( std::move(other.statePtr_) ) { other.statePtr_ = StateT::Create(); } // Move assignment inline TransactionStatus& operator=(TransactionStatus&& other) { if (this != &other) { statePtr_ = std::move(other.statePtr_); other.statePtr_ = StateT::Create(); } return *this; } // Deleted copy ctor & assignment TransactionStatus(const TransactionStatus&) = delete; TransactionStatus& operator=(const TransactionStatus&) = delete; inline void Wait() { assert(statePtr_.Get() != nullptr); statePtr_->Wait(); } private: PtrT statePtr_; template friend void AsyncTransaction(TransactionStatus& status, F&& func); template friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Continuation /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename D2 = D > class Continuation : public REACT_IMPL::ContinuationBase { private: using NodePtrT = REACT_IMPL::NodeBasePtrT; public: using SourceDomainT = D; using TargetDomainT = D2; // Default ctor Continuation() = default; // Move ctor Continuation(Continuation&& other) : Continuation::ContinuationBase( std::move(other) ) {} // Node ctor explicit Continuation(NodePtrT&& nodePtr) : Continuation::ContinuationBase( std::move(nodePtr) ) {} // Move assignment Continuation& operator=(Continuation&& other) { Continuation::ContinuationBase::operator=( std::move(other) ); return *this; } // Deleted copy ctor & assignment Continuation(const Continuation&) = delete; Continuation& operator=(const Continuation&) = delete; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeContinuation - Signals /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename DOut = D, typename S, typename FIn > auto MakeContinuation(TransactionFlagsT flags, const Signal& trigger, FIn&& func) -> Continuation { static_assert(DOut::is_concurrent, "MakeContinuation: Target domain does not support concurrent input."); using REACT_IMPL::SignalContinuationNode; using F = typename std::decay::type; return Continuation( std::make_shared>( flags, GetNodePtr(trigger), std::forward(func))); } template < typename D, typename DOut = D, typename S, typename FIn > auto MakeContinuation(const Signal& trigger, FIn&& func) -> Continuation { return MakeContinuation(0, trigger, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeContinuation - Events /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename DOut = D, typename E, typename FIn > auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& func) -> Continuation { static_assert(DOut::is_concurrent, "MakeContinuation: Target domain does not support concurrent input."); using REACT_IMPL::EventContinuationNode; using REACT_IMPL::AddContinuationRangeWrapper; using REACT_IMPL::IsCallableWith; using REACT_IMPL::EventRange; using F = typename std::decay::type; using WrapperT = typename std::conditional< IsCallableWith>::value, F, typename std::conditional< IsCallableWith::value, AddContinuationRangeWrapper, void >::type >::type; static_assert(! std::is_same::value, "MakeContinuation: Passed function does not match any of the supported signatures."); return Continuation( std::make_shared>( flags, GetNodePtr(trigger), std::forward(func))); } template < typename D, typename DOut = D, typename E, typename FIn > auto MakeContinuation(const Events& trigger, FIn&& func) -> Continuation { return MakeContinuation(0, trigger, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeContinuation - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename DOut = D, typename E, typename FIn, typename ... TDepValues > auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, const SignalPack& depPack, FIn&& func) -> Continuation { static_assert(DOut::is_concurrent, "MakeContinuation: Target domain does not support concurrent input."); using REACT_IMPL::SyncedContinuationNode; using REACT_IMPL::AddContinuationRangeWrapper; using REACT_IMPL::IsCallableWith; using REACT_IMPL::EventRange; using F = typename std::decay::type; using WrapperT = typename std::conditional< IsCallableWith, TDepValues ...>::value, F, typename std::conditional< IsCallableWith::value, AddContinuationRangeWrapper, void >::type >::type; static_assert(! std::is_same::value, "MakeContinuation: Passed function does not match any of the supported signatures."); struct NodeBuilder_ { NodeBuilder_(TransactionFlagsT flags, const Events& trigger, FIn&& func) : MyFlags( flags ), MyTrigger( trigger ), MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... deps) -> Continuation { return Continuation( std::make_shared>( MyFlags, GetNodePtr(MyTrigger), std::forward(MyFunc), GetNodePtr(deps) ...)); } TransactionFlagsT MyFlags; const Events& MyTrigger; FIn MyFunc; }; return REACT_IMPL::apply( NodeBuilder_( flags, trigger, std::forward(func) ), depPack.Data); } template < typename D, typename DOut = D, typename E, typename FIn, typename ... TDepValues > auto MakeContinuation(const Events& trigger, const SignalPack& depPack, FIn&& func) -> Continuation { return MakeContinuation(0, trigger, depPack, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////// /// DoTransaction /////////////////////////////////////////////////////////////////////////////////////////////// template void DoTransaction(F&& func) { using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance().DoTransaction(0, std::forward(func)); } template void DoTransaction(TransactionFlagsT flags, F&& func) { using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////// /// AsyncTransaction /////////////////////////////////////////////////////////////////////////////////////////////// template void AsyncTransaction(F&& func) { static_assert(D::is_concurrent, "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() .AsyncTransaction(0, nullptr, std::forward(func)); } template void AsyncTransaction(TransactionFlagsT flags, F&& func) { static_assert(D::is_concurrent, "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() .AsyncTransaction(flags, nullptr, std::forward(func)); } template void AsyncTransaction(TransactionStatus& status, F&& func) { static_assert(D::is_concurrent, "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() .AsyncTransaction(0, status.statePtr_, std::forward(func)); } template void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func) { static_assert(D::is_concurrent, "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() .AsyncTransaction(flags, status.statePtr_, std::forward(func)); } /******************************************/ REACT_END /******************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Domain definition macro /////////////////////////////////////////////////////////////////////////////////////////////////// #define REACTIVE_DOMAIN(name, ...) \ struct name : \ public REACT_IMPL::DomainBase> {}; \ static REACT_IMPL::DomainInitializer name ## _initializer_; /* A brief reminder why the domain initializer is here: Each domain has a couple of singletons (debug log, engine, input manager) which are currently implemented as meyer singletons. From what I understand, these are thread-safe in C++11, but not all compilers implement that yet. That's why a static initializer has been added to make sure singleton creation happens before any multi-threaded access. This implemenation is obviously inconsequential. */ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Define type aliases for given domain /////////////////////////////////////////////////////////////////////////////////////////////////// #define USING_REACTIVE_DOMAIN(name) \ template \ using SignalT = Signal; \ \ template \ using VarSignalT = VarSignal; \ \ template \ using EventsT = Events; \ \ template \ using EventSourceT = EventSource; \ \ using ObserverT = Observer; \ \ using ScopedObserverT = ScopedObserver; \ \ using ReactorT = Reactor; #endif // REACT_DOMAIN_H_INCLUDED