|
- //
- // Lol Engine
- //
- // Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
- // © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
- //
- // Lol Engine is free software. It comes without any warranty, to
- // the extent permitted by applicable law. You can redistribute it
- // and/or modify it under the terms of the Do What the Fuck You Want
- // to Public License, Version 2, as published by the WTFPL Task Force.
- // See http://www.wtfpl.net/ for more details.
- //
-
- #include <lol/engine-internal.h>
-
- namespace lol
- {
-
- //BaseThreadManager -----------------------------------------------------------
- BaseThreadManager::BaseThreadManager(int thread_max) : BaseThreadManager(thread_max, thread_max)
- { }
-
- BaseThreadManager::BaseThreadManager(int thread_max, int thread_min)
- {
- Setup(thread_max, thread_min);
- }
-
- BaseThreadManager::~BaseThreadManager()
- {
- Stop();
- }
-
- //Base Setup ------------------------------------------------------------------
- void BaseThreadManager::Setup(int thread_max)
- {
- Setup(thread_max, thread_max);
- }
- void BaseThreadManager::Setup(int thread_max, int thread_min)
- {
- #if LOL_FEATURE_THREADS
- m_thread_max = thread_max;
- m_thread_min = thread_min;
- #endif //LOL_FEATURE_THREADS
- }
-
- //Initialize, Ticker::Ref and start the thread --------------------------------
- bool BaseThreadManager::Start()
- {
- #if LOL_FEATURE_THREADS
- ASSERT(!!m_thread_max, "Thread count shouldn't be zero");
-
- if (m_threads.count() > 0)
- return false;
-
- //Add minimum threads
- m_threads.reserve(m_thread_max);
- //AddThreads(m_thread_min);
- AdjustThreadCount(m_thread_min);
- #endif //LOL_FEATURE_THREADS
-
- return true;
- }
-
- //Stop the threads ------------------------------------------------------------
- bool BaseThreadManager::Stop()
- {
- #if LOL_FEATURE_THREADS
- if (m_threads.count() <= 0)
- return false;
-
- //End all threads
- //RemoveThreads((int)m_threads.count());
- AdjustThreadCount(0);
- CleanAddedThread(true);
- CleanRemovedThread(true);
- #endif //LOL_FEATURE_THREADS
-
- return true;
- }
-
- //Work stuff ------------------------------------------------------------------
- bool BaseThreadManager::AddThreadWork(ThreadJob* job)
- {
- return m_jobqueue.try_push(job);
- }
-
- //-----------------------------------------------------------------------------
- void BaseThreadManager::AdjustThreadCount(int count)
- {
- #if LOL_FEATURE_THREADS
- int actual_count = (int)m_threads.count() - m_thread_removed;
- int diff = count - actual_count;
-
- for (int i = 0; i < lol::abs(diff); i++)
- {
- if (diff > 0)
- {
- //Spawn worker threads and ...
- m_threads << new thread(std::bind(&BaseThreadManager::BaseThreadWork, this, std::placeholders::_1));
- m_thread_added++;
- m_thread_added_timer.Reset();
- }
- else
- {
- //Signal worker threads for completion and ...
- m_jobqueue.push(new ThreadJob(ThreadJobType::THREAD_STOP));
- m_thread_removed++;
- m_thread_removed_timer.Reset();
- }
- }
- #endif //LOL_FEATURE_THREADS
- }
-
- //-----------------------------------------------------------------------------
- void BaseThreadManager::CleanAddedThread(bool wait)
- {
- #if LOL_FEATURE_THREADS
- //... Wait for their readiness.
- while (m_thread_added > 0)
- {
- thread* inst = nullptr;
- bool found = false;
- if (wait)
- found = !!(inst = m_spawnqueue.pop());
- else
- found = m_spawnqueue.try_pop(inst);
- if (found)
- m_thread_added--;
- else
- break;
- }
- //Assert if spawning took too much time
- //ASSERT(!(m_thread_added > 0 && m_thread_added_timer.Poll() > 5.f));
- #endif //LOL_FEATURE_THREADS
- }
-
- //-----------------------------------------------------------------------------
- void BaseThreadManager::CleanRemovedThread(bool wait)
- {
- #if LOL_FEATURE_THREADS
- //... Wait for them to quit.
- while (m_thread_removed > 0)
- {
- thread* inst = nullptr;
- bool found = false;
- if (wait)
- found = !!(inst = m_donequeue.pop());
- else
- found = m_donequeue.try_pop(inst);
- if (found)
- {
- m_thread_removed--;
- m_threads.remove_swap_item(inst);
- delete inst;
- }
- else
- break;
- }
- //Assert if stopping took too much time
- //ASSERT(!(m_thread_removed > 0 && m_thread_removed_timer.Poll() > 5.f));
- #endif //LOL_FEATURE_THREADS
- }
-
- //-----------------------------------------------------------------------------
- int BaseThreadManager::GetDispatchCount()
- {
- return (int)m_job_dispatch.count();
- }
- int BaseThreadManager::GetDispatchedCount()
- {
- return m_job_dispatched;
- }
-
- //-----------------------------------------------------------------------------
- void BaseThreadManager::DispatchJob(ThreadJob* job)
- {
- if (job) m_job_dispatch << job;
- }
- void BaseThreadManager::DispatchJob(array<ThreadJob*> const& jobs)
- {
- m_job_dispatch += jobs;
- }
-
- //-----------------------------------------------------------------------------
- bool BaseThreadManager::FetchResult(array<ThreadJob*>& results)
- {
- ThreadJob* result;
- while (m_resultqueue.try_pop(result))
- results << result;
- return results.count() > 0;
- }
-
- //Base thread work function ---------------------------------------------------
- void BaseThreadManager::BaseThreadWork(thread* inst)
- {
- ThreadJob* job = nullptr;
- #if LOL_FEATURE_THREADS
- //Register that the thread has started
- m_statusqueue.push(ThreadStatus::THREAD_STARTED);
- m_spawnqueue.push(inst);
- for (;;)
- #else //LOL_FEATURE_THREADS
- while (m_jobqueue.try_pop(job))
- #endif
- {
- #if LOL_FEATURE_THREADS
- //Retrieve a job
- job = m_jobqueue.pop();
- //Stop thread
- if (job->GetJobType() == ThreadJobType::THREAD_STOP)
- {
- delete job;
- break;
- }
- //Or work
- else
- #endif //LOL_FEATURE_THREADS
- if (*job == ThreadJobType::WORK_TODO)
- {
- #if LOL_FEATURE_THREADS
- m_statusqueue.push(ThreadStatus::THREAD_WORKING);
- #endif //LOL_FEATURE_THREADS
- if (job->DoWork())
- job->SetJobType(ThreadJobType::WORK_SUCCEEDED);
- else
- job->SetJobType(ThreadJobType::WORK_FAILED);
- m_resultqueue.push(job);
- #if LOL_FEATURE_THREADS
- m_statusqueue.push(ThreadStatus::THREAD_IDLE);
- #endif //LOL_FEATURE_THREADS
- }
- }
- #if LOL_FEATURE_THREADS
- //Register that the thread has stopped
- m_statusqueue.push(ThreadStatus::THREAD_STOPPED);
- m_donequeue.push(inst);
- #endif //LOL_FEATURE_THREADS
- }
-
- //-----------------------------------------------------------------------------
- void BaseThreadManager::TickGame(float seconds)
- {
- Entity::TickGame(seconds);
-
- //Start if needed
- Start();
-
- //Dispatch work task
- while (m_job_dispatch.count() > 0 && AddThreadWork(m_job_dispatch[0]))
- {
- m_job_dispatch.remove(0);
- //Keep track of added jobs
- m_job_dispatched++;
- }
-
- //Execute one task per frame if thread are not available
- #if !defined(LOL_FEATURE_THREADS) || !LOL_FEATURE_THREADS
- BaseThreadWork(nullptr);
- #endif // !LOL_FEATURE_THREADS
-
- array<ThreadJob*> result;
- //Fetch and treat results
- if (FetchResult(result))
- {
- for (int i = 0; i < result.count(); i++)
- {
- ThreadJob* job = result[i];
- TreatResult(job);
- //Remove job from count as it has been treated
- m_job_dispatched--;
- }
- }
-
- //Resize thread count if needed
- #if LOL_FEATURE_THREADS
- ThreadStatus status;
- while (m_statusqueue.try_pop(status))
- m_thread_active += status == ThreadStatus::THREAD_IDLE ? -1 : +1;
-
- AdjustThreadCount(lol::clamp(m_job_dispatched, m_thread_min, m_thread_max));
- CleanAddedThread();
- CleanRemovedThread();
- #endif //LOL_FEATURE_THREADS
- }
-
- } /* namespace lol */
|