void TaskScheduler::Quit() { m_quit.store(true); FTLConvertFiberToThread(FTLGetCurrentFiber()); // Wait until all worker threads have finished // // We can't use traditional thread join mechanisms because we can't // guarantee the ThreadIds we started with are the ones we finish with // The 'physical' threads are always there, but the ThreadIds change // when we convert to/from fibers while (m_numActiveWorkerThreads.load() > 0) { std::this_thread::yield(); } std::vector<ThreadType> threadsToCleanUp; ThreadType currentThread = FTLGetCurrentThread(); for (uint i = 0; i < m_numThreads; ++i) { if (m_threads[i] != currentThread) { threadsToCleanUp.push_back(m_threads[i]); } } for (auto &thread : threadsToCleanUp) { FTLCleanupThread(thread); } }
bool TaskScheduler::Initialize(uint fiberPoolSize, GlobalArgs *globalArgs) { for (uint i = 0; i < fiberPoolSize; ++i) { FiberType newFiber = FTLCreateFiber(524288, FiberStart, reinterpret_cast<fiber_arg_t>(globalArgs)); m_fiberPool.enqueue(newFiber); } // Create an additional thread for each logical processor m_numThreads = FTLGetNumHardwareThreads(); m_threads = new ThreadType[m_numThreads]; m_numActiveWorkerThreads.store((uint)m_numThreads - 1); // Create switching fibers for this thread m_fiberSwitchingFibers[FTLGetCurrentThreadId()] = FTLCreateFiber(FTL_HELPER_FIBER_STACK_SIZE, FiberSwitchStart, reinterpret_cast<fiber_arg_t>(&globalArgs->g_taskScheduler)); m_counterWaitingFibers[FTLGetCurrentThreadId()] = FTLCreateFiber(FTL_HELPER_FIBER_STACK_SIZE, CounterWaitStart, reinterpret_cast<fiber_arg_t>(&globalArgs->g_taskScheduler)); // Set the affinity for the current thread and convert it to a fiber FTLSetCurrentThreadAffinity(1); m_threads[0] = FTLGetCurrentThread(); FiberType mainThreadFiber = FTLConvertThreadToFiber(); FTLSetCurrentFiber(mainThreadFiber); // Create the remaining threads for (uint i = 1; i < m_numThreads; ++i) { ThreadStartArgs *threadArgs = new ThreadStartArgs(); threadArgs->globalArgs = globalArgs; threadArgs->threadIndex = i; ThreadType threadHandle; if (!FTLCreateThread(&threadHandle, 524288, ThreadStart, threadArgs, i)) { return false; } m_threads[i] = threadHandle; } return true; }
void TaskScheduler::Run(uint fiberPoolSize, TaskFunction mainTask, void *mainTaskArg) { // Create and populate the fiber pool m_fiberPoolSize = fiberPoolSize; m_fibers = new Fiber[fiberPoolSize]; m_freeFibers = new std::atomic<bool>[fiberPoolSize]; m_waitingFibers = new std::atomic<bool>[fiberPoolSize]; for (uint i = 0; i < fiberPoolSize; ++i) { m_fibers[i] = std::move(Fiber(512000, FiberStart, reinterpret_cast<std::intptr_t>(this))); m_freeFibers[i].store(true, std::memory_order_release); m_waitingFibers[i].store(false, std::memory_order_release); } m_waitingBundles.resize(fiberPoolSize); // 1 thread for each logical processor m_numThreads = FTLGetNumHardwareThreads(); // Initialize all the things m_quit.store(false, std::memory_order_release); m_threads.resize(m_numThreads); m_tls.resize(m_numThreads); // Set the properties for the current thread FTLSetCurrentThreadAffinity(1); m_threads[0] = FTLGetCurrentThread(); // Create the remaining threads for (uint i = 1; i < m_numThreads; ++i) { ThreadStartArgs *threadArgs = new ThreadStartArgs(); threadArgs->taskScheduler = this; threadArgs->threadIndex = i; if (!FTLCreateThread(524288, ThreadStart, threadArgs, i, &m_threads[i])) { printf("Error: Failed to create all the worker threads"); return; } } // Start the main task // Get a free fiber std::size_t freeFiberIndex = GetNextFreeFiberIndex(); Fiber *freeFiber = &m_fibers[freeFiberIndex]; // Repurpose it as the main task fiber and switch to it MainFiberStartArgs mainFiberArgs; mainFiberArgs.taskScheduler = this; mainFiberArgs.MainTask = mainTask; mainFiberArgs.Arg = mainTaskArg; freeFiber->Reset(MainFiberStart, reinterpret_cast<std::intptr_t>(&mainFiberArgs)); m_tls[0].CurrentFiberIndex = freeFiberIndex; m_tls[0].ThreadFiber.SwitchToFiber(freeFiber); // And we're back // Wait for the worker threads to finish for (std::size_t i = 1; i < m_numThreads; ++i) { FTLJoinThread(m_threads[i]); } return; }