/
Ant010ff
/
ffpp
Обзор
Документация
Войти
/
Ant010ff
/
ffpp
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
include/commonpolicy.hpp
711 строк
34 KB
Ant010ff
Version 2.11.3.
08 авг 2026, 16:44
08 авг 2026, 16:44
fd187d3
Код
Авторство
О чём код?
/* This header is part of a Functional Flow Processing Primitives (FFPP) library, version 2.11.3. Official repository: https://gitlab.com/ant010ff/ffpp Licensed under the MIT License <http://opensource.org/licenses/MIT>. SPDX-License-Identifier: MIT Copyright (c) 2021 - 2026 Anton Nasonov <ant010fff @ gmail . com>. */ #ifndef FFPP_COMMON_POLICY_HPP #define FFPP_COMMON_POLICY_HPP namespace FFPP::Concepts { template<typename Type> concept ResourcePolicy = requires { typename Type::template FunctionT<void(), true>; typename Type::template AllocatorT<uint8_t>; typename Type::template SharedT<uint8_t>; typename Type::template WeakT<uint8_t>; typename Type::template UniqueT<uint8_t>; typename Type::IdProviderT; typename Type::IdProviderT::Id; typename Type::IdProviderT::Hash; } && ConstexprInvocableStrict<Type::InterferenceSize, size_t> && ConstexprInvocableStrict<Type::template InvalidValue<size_t>, size_t> && ConstexprInvocableStrict<Type::IdProviderT::InvalidId, typename Type::IdProviderT::Id> && InvocableStrict<Type::template GetAllocator<uint8_t>, typename Type::template AllocatorT<uint8_t>> && InvocableStrict<Type::template AllocateShared<uint8_t>, typename Type::template SharedT<uint8_t>> && InvocableStrict<Type::template AllocateUnique<uint8_t>, typename Type::template UniqueT<uint8_t>> && InvocableStrict<Type::template AsWeak<uint8_t>, typename Type::template WeakT<uint8_t>, typename Type::template SharedT<uint8_t>> && InvocableStrict<Type::IdProviderT::GenerateId, typename Type::IdProviderT::Id> && InvocableStrict<Type::SetThreadAffinity, bool, ThreadHandleT, size_t> ;//ResourcePolicy }//FFPP::Concepts namespace FFPP { template<Concepts::FlagsEnum TFlags> inline constexpr bool operator ! (TFlags flg) { return flg == TFlags::Undefined ? true : false; } template<Concepts::FlagsEnum TFlags> inline constexpr TFlags operator ~ (TFlags flg) { return static_cast<TFlags>(~static_cast<uint32_t>(flg)); } template<Concepts::FlagsEnum TFlags> inline constexpr TFlags operator | (TFlags left, TFlags right) { return static_cast<TFlags>(static_cast<uint32_t>(left) | static_cast<uint32_t>(right)); } template<Concepts::FlagsEnum TFlags> inline constexpr TFlags operator & (TFlags left, TFlags right) { return static_cast<TFlags>(static_cast<uint32_t>(left) & static_cast<uint32_t>(right)); } template<Concepts::FlagsEnum TFlags> inline consteval bool TestModeFlags(TFlags flgGiven, TFlags flgTest, bool bAll = false) { if(bAll) return (flgGiven & flgTest) == flgTest; else return (flgGiven & flgTest) != TFlags::Undefined; } template<Concepts::FlagsEnum TFlags> [[deprecated("Use TestModeFlags() instead.")]] inline consteval bool IsModeFlags(TFlags flgGiven, TFlags flgTest, bool bAll = false) { return TestModeFlags(flgGiven, flgTest, bAll); } }//FFPP namespace FFPP { //Context dependant flags. Can be applied to functional sequence objects, their methods and operations involving functional //sequences. Flags which are not supported in current context will be ignored. enum class Flags : uint32_t { Undefined = 0x00000000, //Generic functional sequence. Also used internally as operation context flag (FunctionalBroker::Finalize). Generic = 0x00000001, //Ignore queue limit when emitting message to target. Special attention must be paid when sending obligate message to self //or other instance bound to the same thread pool when using fixed-capacity ring-queue (see QueueFlags::Fixed). Obligate = 0x00000002, Unlimited = Obligate, //Capture weak reference to acceptor. Weak = 0x00000004, //Keep value or any associated context until manual finalization. Keep = 0x00000008, //BrokerTaskLoop instance is an external signal receiver. Received value will be kept until instance finalization. Signal = Keep, //BrokerTaskLoop is a junction point, which can receive value(s) been emitted before corresponding handler registration. Junction = 0x00000010, //Enables intermediate queue in case of indirect message/event emission registered with FunctionalBroker::Accept. By default //message is enqueued directly into acceptor's queue. Decouple = 0x00000020, //Asynchronous operation, like in context of broker's thread pool (FunctionalBroker::Finalize). Async = Decouple, //Submit and complete chain, not a property of chain instance. Complete = 0x00000100, //Chain with deferred submission for processing. Deferred = 0x00000200, //Incomplete (reusable) chain, property of chain instance. Incomplete = Keep, //Unlocked event emission with FunctionalBroker::Emit using task table (StripedTable) stripe local copy (not used by default). Unlocked = 0x00400000, //Enables concurrent processing for one and the same functional sequence instance in case of mutiple concurrent activations. //Requires either stateless, or immutable, or manually synchronized handlers. Intended for use with FunctionalBroker::Receive, //ignored for functional chains. Concurrent = 0x00800000, //Operation with max priority. Priority = 0x01000000, //Infinite timeout, do not track timeout. Infinite = 0x04000000, //Operation with infinite wait. Wait = Infinite, //Unique entity (like new StripedTable entry acquisition with unique lock) or nonconcurrent operation. Unique = 0x08000000, //BrokerTaskLoop instance is an event acceptor dongle with empty functional sequence (library internal usage). Dongle = 0x40000000, //Treat FunctionalLoop instance as initially commited or ready to be commited (ready for processing, library internal usage). Commit = 0x80000000, }; }//FFPP namespace FFPP { //Option flags for queue implementations. enum class QueueFlags : uint32_t { Undefined = 0x00000000, //Fixed capacity queue. Fixed = Undefined, //Queue capacity enabled to grow. Grow = 0x00000001, //Queue capacity enabled to shrink. Shrink = 0x00000002, //Full dynamic capacity queue. Dynamic = Grow | Shrink, //Enables value swap semantics (if implemented in the queue). Expects TValue::swap(TValue&) method presence and TValue to //be default-constructable. Can be ignored in favor of move semantics if the latter enables noexcept operation. In this //case TValue::swap should be implemented as noexcept as well. Targets primarily std::move_only_function (when C++ 23 //available) and std::function (when C++ 23 is not available) which are used as an internal task representation in the //library. Can be used to enable exception-free operation of a RingQueue in a fixed-capacity mode (when used a task queue). Swap = 0x00000100, //Wait in case fixed-capacity queue overflow on enqueueing, fail otherwise. Wait = 0x00100000, //Obligate enqueue specifier (| Wait). Obligate = 0x10000000, //Wait on overflow when enqueueing (| Wait). Overflow = 0x20000000, //Wait on empty when dequeueing (| Wait). Empty = 0x40000000, Default = Grow }; }//FFPP namespace FFPP { //Option flags for mutex implementations. enum class MutexFlags : uint32_t { Undefined = 0x00000000, //Enables exceptions. Throw = 0x00000001, //Enables recursive locking attempts checks. CheckRecursion = 0x00000002 | Throw, //Enables sleep (wait on atomic) after spin periods, using busy-wait only if not specified (spinlock mode). Sleep = 0x00000004, //Enables split internal layout for ticket-lock mutexes. That is the use of distinct cache lines for `enter` and `leave` //ticket counters. May improve performance in case of a high contention on a single mutex. Otherwise both counters will be //placed in a single cache line which is better in low contention scenarios. Applies to TicketMutex and StripedTicketMutex //(per ticket pair slot). Split = 0x00000008, HighContention = Split, #if FFPP_ENABLE_DEBUG Default = CheckRecursion #else Default = Undefined #endif }; }//FFPP namespace FFPP { //FunctionalQueuePool (thread pool) task dispatch control flags. enum class DispatchFlags : uint32_t { Undefined = 0x00000000, //Indirect task dispatch mode. Decouples calling thread from task dispatch at the cost of dispatch latency increase. Not //recommended in high contention environment. If not specified a direct dispatch in a calling or processing thread (in case //of SchedulingFlags::Deferred mode) will be used which provides lower latency (library's default choice). Indirect = 0x80000000, Async = Indirect, //Provides lowest dispatch latency and best overall responsiveness available in direct dispatch mode. Enforces fixed size //thread pool and disables processing threads dynamic lifetime control. Disabled by default. Static = 0x40000000, //Binds each functional sequence instance to processing thread (selected automatically by dispatcher) requires static thread //pool (DispatchFlags::Static). Bind = 0x20000000 | Static, //Prefer locality over parallelism when performing internal task emission, that is processing thread emitting new task will //enqueue it in its own queue. Disables task offloading (SchedulingFlags::Offload, acting as SchedulingFlags::Deferred in //this case). Compatible with any other dispatch mode including task-stealing (Steal), that is lower-loaded threads may //steal tasks previously enqueued to self by currently high-loaded threads. Locality = 0x10000000, //Sets affinity for each thread with the use of ResourcePolicyT::SetThreadAffinity static method (can be redefined by user). //By default binds each thread to a single hardware thread selected with a round-robin approach (if number of threads is //greater than number of hardware threads available). Affinity = 0x08000000, //Aligns threads limit to upper power-of-2, enables processing thread access optimizations. Align2 = 0x04000000, //Aligns threads limit to lower power-of-2, enables processing thread access optimizations. Lower2 = 0x02000000 | Align2, //Enables Join-Idle-Queue algorithm implementation with the use of FFPP::RingQueue in a fixed-capacity mode (that is lock- //free). For each thread group of a given size (PoolPolicy::ThreadGroupSize()) pool maintains a per-group idle-queue object //(JIQ-segment) containing indexes of idle threads of this group. Each thread when going to idle state (task-stealing failed //or disabled) enqueues its id into corresponding group's JIQ-segment. On each internal task emission current JIQ-segment //checked first, if this fails all segments are checked, in case of external task emission all segments checked immediately. //When number of segments is greater than threshold (internal constant) segments are iterated bidirectionally starting from //a random index. If there are no idle threads JIQ-checks skipped immediately and default dispatch logic invoked as specified //with other DispatchFlags (must be combined with at least one of: Optimal, Random, Random2 or RandomL2). Overrides locality //and topology settings for internal emissions if there are idle threads in queue. IdleQueue = 0x00800000, //Enables lightweight work-stealing-like processing (task-stealing) which implies processing threads mutual assistance when //threads after completing own tasks tries to steal pending tasks of busy ones instead of going idle. Requires deferred //scheduling (SchedulingFlags::Deferred), incompatible with task to thread binding (DispatchFlags::Bind). Can be combined //with (Random, Hypercube, Sparse, Reshuffle) for additional capabilities. //Steal complexity: //- O(nThreadsLimit), default; //- O(log(nThreadsLimit)) or O(log(log(nThreadsLimit))) amortized (Sparse | Hypercube); //- O(log(log(nThreadsLimit))), Sparse; //- O(1), Random, Random2; //- O(log(nThreadsLimit)), Hypercube, RandomL2. Steal = 0x00400000, //Enables repetitive steal attempts with customizable back-off (exponential jittered back-off by default) which is used to //provide short busy-wait between attempts and control number of attempts (4 by default, see FFPP::PoolPolicy) Resteal = 0x00200000 | Steal, //Enables pool-wide task-stealing across topology boundaries when topology-constrained steal failed. //Steal complexity: //- O(nThreadsLimit); WideSteal = 0x00100000 | Steal, //Introduces sparse L-dimensional (L = log(log(nThreadsLimit))) randomized topology constraints covering intra-pool thread //access at internal task dispatch and steal. That is any thread when performing inter-thread access (new task emission, //task offloading, task-stealing) is limited within its random topology group of size L. Lowers contention in high-load //scenarios with intensive inter-actor message exchange enhancing dispatch latencies and overall actor system throughput. //Internal dispatch complexity: //- O(log(log(nThreadsLimit))), Optimal | Sparse, RandomL2 | Sparse; //- O(1), Random | Sparse, Random2 | Sparse; //Steal complexity: //- O(log(log(nThreadsLimit))), Sparse | Steal. Sparse = 0x00010000, //Introduces hypercube (with Align2) or wrapped hypercube (no Align2) topology constraints covering intra-pool thread access //at internal task dispatch and steal. Lowers contention in high-load scenarios with intensive inter-actor message exchange //enhancing dispatch latencies and overall actor system throughput. Can be combined with (Sparse, Optimal, Reshuffle, Align2, //Lower2) for additional capabilities. In combination with Sparse (Hypercube | Sparse) enables a "sparse hypercube" with //additional per-thread random topology mask restricting access to log(log(nThreadsLimit)) neighbour threads. Addition of //a Reshuffle flag (Hypercube | Sparse | Reshuffle) enables dynamic topology mask updates. //Internal dispatch complexity: //- O(log(nThreadsLimit)), Optimal | Hypercube; //- O(log(log(nThreadsLimit))), Optimal | Sparse | Hypercube; //- O(1), Random | Hypercube, Random2 | Hypercube. //Steal complexity: //- O(log(nThreadsLimit)), default; //- O(log(nThreadsLimit)) or O(log(log(nThreadsLimit))) amortized, Sparse | Hypercube. Hypercube = 0x00020000, //Enables dynamic randomized topology in combination with (Sparse), (Sparse | Hypercube) with up to log(log(nThreadsLimit)) //dimensions. That is current topology can be reshuffled when: //- pool's busy thread (with pending tasks in its own queue) emitting new task discovers itself to be a target processing // thread (its topology neighbor threads are more loaded), when combined with: (Optimal), (Optimal | Random), (Random2), // (RandomL2); //- thread failed to steal task from its current neighbor threads (enabled by current topology configuration), when // combined with (Steal). Reshuffle = 0x00008000, //Topology constraints mask. Topology = Hypercube | Sparse, //Dispatch-time opportunism: reusing idle threads instead of running new ones, running new thread if there are no idle ones, //selecting thread with minimal load if threads limit reached. If task-stealing enabled uses linear stealing algorithm by //default. Can be combined with (Hypercube, Random, Random2, RandomL2, Reshuffle) for additional capabilities. In case of //intra-pool thread access (new task dispatch, task steal) respects topology constraints of (Hypercube), (Spars | Hypercube) //and (Spars). When specified with one of randomized (Random, Random2, RandomL2) and topology (Sparse, Hypercube, //Sparse | Hypercube) modes randomized dispatch will be used for external task emissions while optimal will be enabled for //internal emissions and offloads. Best for dynamic thread pools, one of universal choices (Optimal, Random2, RandomL2). //External dispatch complexity: //- O(nThreadsLimit), default. //Internal dispatch complexity: //- O(log(log(nThreadsLimit))), Optimal | Sparse; //- O(log(nThreadsLimit)), Optimal | Hypercube; //- O(log(log(nThreadsLimit))), Optimal | Sparse | Hypercube. //Steal complexity: //- O(nThreadsLimit), default; //- O(1), Optimal | Random | Steal, Optimal | Random2 | Steal; //- O(log(nThreadsLimit)), Optimal | RandomL2 | Steal; //- O(log(log(nThreadsLimit))), Optimal | Sparse | Steal; //- O(log(nThreadsLimit)), Optimal | Hypercube | Steal; //- O(log(nThreadsLimit)) or O(log(log(nThreadsLimit))) amortized, Optimal | Random | Hypercube | Steal. Optimal = 0x00000001, //Running and using each thread in pool one by one evenly filling whole pool within its limit. Better suits some homogeneous //tasks, not a universal choice. Overrides (Optimal), enforces (Fill) mode. //Dispatch complexity (external/internal): O(1). //Steal complexity: //- O(nThreadsLimit), default; //- O(1), Fill | Random; //- O(1), Fill | Random2; //- O(log(nThreadsLimit)), Fill | RandomL2; //- O(log(log(nThreadsLimit))), Fill | Sparse | Steal; //- O(log(nThreadsLimit)), Fill | Hypercube; //- O(log(nThreadsLimit)) or O(log(log(nThreadsLimit))) amortized, Fill | Random | Hypercube. Fill = 0x00000002 | Optimal, //Enables random task dispatch with single probe if Optimal not specified. Selects processing thread using XoShiRo256** //(XoShiRo128** fallback for 32-bit builds) PRNG, combine with DispatchFlags::Steal, SchedulingFlags::Offload or both. //When task-stealing enabled also enforces randomized stealing algorithm. Can be combined with (Hypercube, Reshuffle) for //additional capabilities. When used with (Hypercube) enables additional randomized topology constraints mask with up to //log(log(nThreadsLimit)) dimensions which helps to mitigate internal pool contention in cases of intensive inter-actor //message exchange. When used with (Optimal) and one of topology modes (Sparse, Hypercube, Sparse | Hypercube) enables //randomized dispatch for external emissions while using optimal for internal emissions and offloads. Good for static //thread pools (Static). //Dispatch complexity (external/internal): O(1). //Steal complexity: //- O(1), default; //- O(log(nThreadsLimit)) or O(log(log(nThreadsLimit))) amortized, Random | Hypercube. Random = 0x00000004, //Enables task dispatch with optimal choice between two random variants if (Optimal) not specified (power-of-two choices), //add DispatchFlags::Steal, SchedulingFlags::Offload or both. When task-stealing enabled also enforces randomized stealing //algorithm. Can be combined with (Hypercube, Reshuffle) for additional capabilities. When used with (Optimal) and one of //topology modes (Sparse, Hypercube, Sparse | Hypercube) enables randomized dispatch for external emissions while using //optimal for internal emissions and offloads. Good for static thread pools (Static), one of universal choices (Optimal, //Random2, RandomL2), consider for high core count CPUs. //Dispatch complexity (external/internal): O(1). //Steal complexity: //- O(1), default; //- O(log(nThreadsLimit)) or O(log(log(nThreadsLimit))) amortized, Random2 | Hypercube. Random2 = 0x00000008 | Random, //Enables task dispatch with optimal choice from log(nThreadsLimit) random variants if (Optimal) not specified, combine with //DispatchFlags::Steal, SchedulingFlags::Offload or both. When task-stealing enabled also enforces randomized stealing //algorithm. When used with (Optimal) and one of topology modes (Sparse, Hypercube, Sparse | Hypercube) enables randomized //dispatch for external emissions while using optimal for internal emissions and offloads. Good for static thread pools //(Static), one of universal choices (Optimal, Random2, RandomL2), consider for high core count CPUs. //Dispatch complexity (external/internal): //- O(log(nThreadsLimit)), default; //- O(log(log(nThreadsLimit))), RandomL2 | SParse, RandomL2 | Hypercube; //Steal complexity: //- O(Log(nThreadsLimit)), default; //- O(log(nThreadsLimit)) or O(log(log(nThreadsLimit))) amortized, RandomL2 | Hypercube. RandomL2 = 0x00000010 | Random2, //Random task dispatch and steal modes mask. Randomized = RandomL2 | Random2 | Random, //Special case of a single threaded pool. Processing thread never stops and goes to sleep when idle. Single = 0xFFFFFFFF, //Basic dynamic thread pool with variable number of running threads depending on the current load, linear task dispatch //and steal. Suitable for irregular jobs with external task emission (no or rare internal thread-to-thread addressing, //like inter-actor message emission). Linear = Optimal | Resteal, //Linear mode variation with randomized logarithmic task-stealing and linear dispatch. LinearRandom = Linear | RandomL2, //Thread pool with fixed number of threads, linear task dispatch and steal. By default binds each thread to a single hardware //thread selected with a round-robin approach (if number of threads is greater than number of hardware threads available). LinearStatic = Linear | Static | Affinity, //LinearStatic mode variation with internal task locality preference. LinearLocal = Linear | Static | Affinity | Locality, //LinearStatic mode variation with task-to-thread binding and thus task-stealing disabled. LinearBound = Bind | Optimal | Affinity, //Thread pool with fixed number of threads, randomized task dispatch and randomized task-stealing. By default binds each //thread to a single hardware thread selected with a round-robin approach (if number of threads is greater than number of //hardware threads available). Provides more consistent dispatch latencies due to the use of Random2. RandomStatic2 = Static | Affinity | IdleQueue | Random2 | Resteal, RandomStaticL2 = Static | Affinity | IdleQueue | RandomL2 | Resteal, //RandomStatic2 mode variation with internal task locality preference. RandomLocal2 = Static | Affinity | IdleQueue | Random2 | Resteal | Locality, RandomLocalL2 = Static | Affinity | IdleQueue | RandomL2 | Resteal | Locality, //RandomStatic2 mode variation with task-to-thread binding and thus task-stealing disabled. RandomBound2 = Bind | Affinity | IdleQueue | Random2, RandomBoundL2 = Bind | Affinity | IdleQueue | RandomL2, //Randomized sparse topology constrained mode, provides descent latencies and high overall task throughput. Uses idle-queues, //randomized logarithmic probes for external task emissions and optimal linear selection for internal emissions/offloads. //Mitigates internal contention enhancing performance of large actor systems while maintaining reasonable latencies for //external task dispatching when running large pools. By default binds each thread to a single hardware thread selected //with a round-robin approach (if number of threads is greater than number of hardware threads available). Intended for //long-running jobs, can be suboptimal in cases of short parallel computations (as well as any other sparse topology mode). SparseStatic = Sparse | Reshuffle | Static | Affinity | IdleQueue | RandomL2 | Optimal | Resteal, //SparseStatic mode variation with internal task locality preference. SparseLocal = SparseStatic | Locality, //SparseStatic mode variation with task-to-thread binding and thus task-stealing disabled. SparseBound = Sparse | Reshuffle | Bind | Affinity | IdleQueue | RandomL2 | Optimal, //Randomized sparse hypercube based mode, provides descent latencies and high overall task throughput. Uses idle-queues //randomized logarithmic probes for external task emissions and optimal linear selection for internal emissions/offloads. //Mitigates internal contention enhancing performance of large actor systems while maintaining reasonable latencies for //external task dispatching when running large pools. By default binds each thread to a single hardware thread selected //with a round-robin approach (if number of threads is greater than number of hardware threads available). Intended for //long-running jobs, can be suboptimal in cases of short parallel computations (as well as any other sparse topology mode). HyperSparse = Hypercube | Sparse | Reshuffle | Static | Affinity | IdleQueue | RandomL2 | Optimal | Resteal, //Full hypercube based mode. Uses idle-queues, randomized logarithmic probes for external task emissions and optimal linear //selection for internal emissions/offloads. By default binds each thread to a single hardware thread selected with a round- //robin approach (if number of threads is greater than number of hardware threads available). Intended for long-running jobs, //can be suboptimal in cases of short parallel computations (as well as any other sparse topology mode). HyperStatic = Hypercube | Static | Affinity | IdleQueue | RandomL2 | Optimal | Resteal, //HyperStatic mode variation with internal task locality preference. HyperLocal = HyperStatic | Locality, //HyperStatic mode variation with task-to-thread binding and thus task-stealing disabled. HyperBound = Hypercube | Bind | Affinity | IdleQueue | RandomL2 | Optimal, //Dynamic thread pool mode with variable number of running threads depending on the current load with no affinity. Provides //reasonable balance between performance and system resources consumption. Scales well from small to medium thread counts in //case of external tasks prevalence, better in cases of actor systems with inter-actor messaging. Good as a starting point, //fits best for irregular loads, suboptimal in high-load scenarios. Adaptive = Sparse | Reshuffle | Optimal | Resteal, Irregular = Adaptive, //Dynamic thread pool mode with growing number of running threads depending on the current load. When the load grows pool //is enabled to grow within specified limits and keeps its current number of threads when load goes down. During idle //periods after timeout expiration pool shrinks, that is stops all its threads (except dispatcher) and returns to initial //state. Under high load when all threads are active and there are no idle ones (waiting and registered in idle-queue) it //uses efficient dispatch selecting less-loaded thread from log(nThreadsLimit) random variants for external tasks and fast //optimal dispatch for internal tasks (log(log(nThreadsLimit))) inside its dynamic sparse topology. Fits better for cases //with large growing pools than Adaptive mode. Elastic = Affinity | Sparse | Reshuffle | Optimal | RandomL2 | IdleQueue | Resteal, //Low-latency dispatch both for external and internal tasks, as well as task-stealing and offloading, due to double random //probes in a static pool with per-thread affinity. LL = RandomStatic2, //Theoretically, should provide highest task throughput possible for current implementation without sacrificing internal //rebalance capabilities. HT = HyperSparse | Locality, //Pool intended for stateful tasks processing, internal rebalancing completely disabled. That is no task-stealing and //offloading, new internal tasks will be bound to their corresponding emitting threads when pool is under full load (no //idle threads). In contrast each new external task will be dispatched to currently optimal thread (least-loaded, with //lowest queue depth). Idle-queue prevents possible underloading and mitigates dispatch latency after idle periods when //new tasks emitted (either internal or external). STP = Bind | Affinity | Locality | IdleQueue | Optimal, Default = Adaptive }; };//FFPP namespace FFPP { //Executable and FunctionalSequence (for Actor and similar classes) scheduling control flags. enum class SchedulingFlags : uint32_t { Undefined = 0x00000000, Disabled = Undefined, //Always use immediate scheduling in context of task/message enqueueing thread. Selects processing thread at the moment of //enqueue (FunctionalSequence::Enqueue or FunctionalSequence::Emit methods call). Each FunctionalSequence instance may //appear multiple times in processing thread's queue it was bound to on first enqueue (interleaving with other instances). //Puts significant pressure on pool thread's queues. Will be ignored when Deferred specified. Immediate = 0x00000001, //Use immediate scheduling on enqueue into empty FunctionalSequence, while nonempty sequence will schedule itself after //current task/message processing (deferred scheduling). Requires special handling of OnEnqueued and OnDequeued events //(see Actor class). Improves overall efficiency of actor system in case of high message rate (~million per second and //above), provides the lowest latency possible for message enqueueing threads and lowest pressure on pool thread's queues. //Each FunctionalSequence (Actor) instance appears only once (per pending task) in selected processing thread's queue. Deferred = 0x00000002, //Enables offloading FunctionalSequence (Actor, FunctionalLoop) instance processing from pool's current thread to the free //one (if there is any) in case there are pending tasks in its queue. Offloading is applied on per-class basis making it //able to choose between cache locality (disabled) or enforced parallelism (enabled) optimizations depending on the //properties of each class without the need of different pool instances (with different policies). Ignored in case of //DispatchFlags::Bind dispatch mode. Offload = 0x00000004 | Deferred, Offloading = Offload, //Enables concurrent scheduling mode without executable to executor binding. Not applicable to FunctionalSequence objects //(Actors, functional chains and others). Concurrent = 0x00000010, //Strictly one time dispatch executable object (fire and forget task). Not applicable to FunctionalSequence objects (Actors, //functional chains and others). Once = 0x00000020 | Concurrent, //Binds functional sequence instance to processing thread (selected automatically by dispatcher), requires static thread //pool (DispatchFlags::Static). Bind = 0x80000000, Default = Offload }; }//FFPP namespace FFPP { template<template<typename> typename TAllocator = std::allocator> struct ResourcePolicy { template<typename TSignature, bool t_bUnique = true> using FunctionT = FFPP::Function<TSignature, t_bUnique>; template<typename Type> using AllocatorT = TAllocator<Type>; template<typename Type> using PointerT = std::allocator_traits<AllocatorT<Type>>::pointer; template<typename Type> using SharedT = std::shared_ptr<Type>; template<typename Type> using UniqueT = std::unique_ptr<Type, FunctionT<void(PointerT<Type>)>>; template<typename Type> using WeakT = std::weak_ptr<Type>; using IdProviderT = IdProvider; static constexpr size_t InterferenceSize() { return FFPP_DEFAULT_INTERFERENCE_SIZE; } template<std::integral Type> static constexpr Type InvalidValue() { return std::numeric_limits<Type>::max(); } template<typename Type> static AllocatorT<Type> GetAllocator() { return { }; } template<typename Type, typename... TArguments> static SharedT<Type> AllocateShared(TArguments&&... args) { return std::allocate_shared<Type>(GetAllocator<Type>(), std::forward<TArguments>(args)...); } template<typename Type, typename... TArguments> static UniqueT<Type> AllocateUnique(TArguments&&... args) { using AllocatorTraitsT = std::allocator_traits<AllocatorT<Type>>; //static_assert(std::is_base_of_v<AllocatorTraitsT::value_type, std::remove_cv_t<Type>>, "Invalid allocator value_type."); auto aAllocator = GetAllocator<Type>(); auto pAllocated = AllocatorTraitsT::allocate(aAllocator, 1); #if FFPP_NO_EXCEPTIONS if(!pAllocated) return { }; #else if(!pAllocated) throw std::bad_alloc(); #endif #if !FFPP_NO_EXCEPTIONS try #endif { AllocatorTraitsT::construct(aAllocator, std::to_address(pAllocated), std::forward<TArguments>(args)...); return { pAllocated, [aAllocator] (auto pAllocated) mutable { AllocatorTraitsT::destroy(aAllocator, std::to_address(pAllocated)); AllocatorTraitsT::deallocate(aAllocator, pAllocated, 1); } }; } #if !FFPP_NO_EXCEPTIONS catch(...) { AllocatorTraitsT::deallocate(aAllocator, pAllocated, 1); throw; } #endif } template<typename Type> static WeakT<Type> AsWeak(SharedT<Type> const& sp) { return WeakT<Type>(sp); } static bool SetThreadAffinity(ThreadHandleT hThread, size_t iThread) { return FFPP::SetThreadHardwareAffinity(hThread, iThread); } };//ResourcePolicy struct DefaultResourcePolicy { template<typename TSignature, bool t_bUnique = true> using FunctionT = FFPP::Function<TSignature, t_bUnique>; template<typename Type> using AllocatorT = std::allocator<Type>; template<typename Type> using PointerT = std::allocator_traits<AllocatorT<Type>>::pointer; template<typename Type> using SharedT = std::shared_ptr<Type>; template<typename Type> using UniqueT = std::unique_ptr<Type>; template<typename Type> using WeakT = std::weak_ptr<Type>; using IdProviderT = IdProvider; static constexpr size_t InterferenceSize() { return FFPP_DEFAULT_INTERFERENCE_SIZE; } template<std::integral Type> static constexpr Type InvalidValue() { return std::numeric_limits<Type>::max(); } template<typename Type> static AllocatorT<Type> GetAllocator() { return { }; } template<typename Type, typename... TArguments> static SharedT<Type> AllocateShared(TArguments&&... args) { return std::make_shared<Type>(std::forward<TArguments>(args)...); } template<typename Type, typename... TArguments> static UniqueT<Type> AllocateUnique(TArguments&&... args) { return std::make_unique<Type>(std::forward<TArguments>(args)...); } template<typename Type> static WeakT<Type> AsWeak(SharedT<Type> const& sp) { return WeakT<Type>(sp); } static bool SetThreadAffinity(ThreadHandleT hThread, size_t iThread) { return FFPP::SetThreadHardwareAffinity(hThread, iThread); } };//DefaultResourcePolicy }//FFPP namespace FFPP { namespace dtl { inline char g_jszMetadata[] = #if FFPP_ENABLE_METADATA R"({"meta": "FFPP", "version": "2.11.3", "license": "MIT", "author": "Ant010ff", "contacts": "ant010fff@gmail.com"})" #else R"()" #endif ; template<bool t_bForce = true> struct Metadata { static inline const auto& c_jszMetadata = g_jszMetadata; static std::string const& Get() { static const std::string s_strMetadata = c_jszMetadata; return s_strMetadata; } }; template struct Metadata<true>; inline const auto c_jszMetadata = Metadata<>::c_jszMetadata; } inline std::string const& Metadata() { return dtl::Metadata<>::Get(); } }//FFPP #endif//FFPP_COMMON_POLICY_HPP