// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "UnsyncCommon.h" #include "UnsyncThread.h" namespace unsync { class FScheduler; struct FTaskGroup; extern FScheduler* GScheduler; struct FSchedulerSemaphore { UNSYNC_DISALLOW_COPY_ASSIGN(FSchedulerSemaphore) FSchedulerSemaphore(FScheduler& InScheduler, uint32 MaxCount); bool TryAcquire() { return Native.try_acquire(); } void Acquire(bool bAllowTaskExecution = true); void Release(); FScheduler& Scheduler; std::counting_semaphore Native; }; class FScheduler { public: UNSYNC_DISALLOW_COPY_ASSIGN(FScheduler) using FTaskFunction = FThreadPool::FTaskFunction; static constexpr uint32 MAX_NETWORK_TASKS = 8; FScheduler(uint32 InNumWorkerThreads); ~FScheduler(); const uint32 NumWorkerThreads; FSchedulerSemaphore NetworkSemaphore; FTaskGroup CreateTaskGroup(FSchedulerSemaphore* ConcurrencyLimiter = nullptr); void TryExecuteTask() { ThreadPool.TryExecuteTask(); } bool ExecuteTasksUntilIdle() { uint64 NumExecuted = 0; while (ThreadPool.TryExecuteTask()) { NumExecuted++; } return NumExecuted != 0; } template void PushTask(TaskFunction&& Fun, bool bAllowImmediateExecution = true) { ThreadPool.PushTask(std::forward(Fun), bAllowImmediateExecution); } private: FThreadPool ThreadPool; }; struct FTaskGroup { UNSYNC_DISALLOW_COPY_ASSIGN(FTaskGroup) template void run(F InFunction) { ++NumStartedTasks; const bool bAcquired = Semaphore && Semaphore->TryAcquire(); if (!Semaphore || bAcquired) { ThreadPool.PushTask( [Semaphore = this->Semaphore, bAcquired, &NumStartedTasks = this->NumStartedTasks, &NumFinishedTasks = this->NumFinishedTasks, Function = std::forward(InFunction)]() -> void { Function(); if (Semaphore && bAcquired) { Semaphore->Release(); } ++NumFinishedTasks; }); } else { InFunction(); ++NumFinishedTasks; } } void wait() { while (NumFinishedTasks.load() != NumStartedTasks.load()) { ThreadPool.TryExecuteTask(); } } FThreadPool& ThreadPool; std::atomic NumStartedTasks; std::atomic NumFinishedTasks; FSchedulerSemaphore* Semaphore = nullptr; ~FTaskGroup() { wait(); }; private: friend FScheduler; FTaskGroup(FThreadPool& InThreadPool, FSchedulerSemaphore* InSemaphore) : ThreadPool(InThreadPool), Semaphore(InSemaphore) {} }; template inline void ParallelForEach(IT ItBegin, IT ItEnd, FT F) { FTaskGroup TaskGroup = GScheduler->CreateTaskGroup(); for (; ItBegin != ItEnd; ++ItBegin) { auto* It = &(*ItBegin); TaskGroup.run([&F, It]() { F(*It); }); } TaskGroup.wait(); } template inline void ParallelForEach(T& Container, FT F) { ParallelForEach(std::begin(Container), std::end(Container), F); } } // namespace unsync