/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "async/job.h" #include "memory/memory.h" #include "log/log.h" using namespace GS::ASync; using namespace GS::Threading; #ifndef _DEBUG #define __ENABLE_ITT_API__ 0 #endif #if __ENABLE_ITT_API__ #include "ittnotify.h" static __itt_domain *domain = NULL; #endif //------------------------------------------------------------------------------ void JobWorkerThread::Execute() { running.Set(1); Thread::SetName(GS::String::Format("Job Worker Thread %d", worker_id)); while (running.Get() == 1) { // Execute as much jobs as possible until starvation. while (manager.ExecutePendingJob(worker_id)); // Wait for notification on the queue event. manager.job_queued_event.Wait(); } running.Set(0); } void JobWorkerThread::Stop() { running.Set(2); } bool JobWorkerThread::IsRunning() const { return running.Get() > 0; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool JobManager::CreateJobThreadPool(uint count) { FreeJobThreadPool(); if (!pool.Allocate(count)) return false; for (uint n = 0; n < count; ++n) if ((pool[n] = new JobWorkerThread(*this, n + 1)) == NULL) __ERR__(__LOG_E__ << "Failed to allocate a job worker thread.\n", false) // Create workers. for (uint n = 0; n < count; ++n) if (!pool[n]->Start()) __LOG_W__ << "Failed to create worker thread " << pool[n]->GetWorkerId() << ".\n"; return true; } void JobManager::FreeJobThreadPool() { // Set thread exit flag. for (uint n = 0; n < pool.GetCount(); ++n) pool[n]->Stop(); // Trigger event so that the thread processes the exit flag. for (uint n = 0; n < pool.GetCount(); ++n) job_queued_event.Trigger(); // Join threads. for (uint n = 0; n < pool.GetCount(); ++n) if (!pool[n]->IsRunning()) delete pool[n]; pool.Free(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool JobManager::EnqueueJob(Job *job, JobGroup *group) { if (pool.GetCount() == 0) { job->Execute(0); job->done.Set(1); } else { job->done.Set(0); if (group) { MutexLock lock(group->job_list_mutex); group->job_list.Add(job); } { #if __USE_LOCK_FREE_JOB_QUEUE__ while (!pending_queue.enqueue(job)) {} #else nMutexLock lock(pending_queue_mutex); pending_queue.Push(job); #endif } } job_queued_event.Trigger(); // wake workers return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool JobManager::JoinJob(Job *job, bool blocking) { if (job) while (job->done.Get() == 0) // spinlock if (!blocking) return false; return true; } bool JobManager::JoinGroup(JobGroup *group, bool blocking) { if (group) for (bool done = false; !done; ) // spinlock { done = true; { MutexLock glock(group->job_list_mutex); ListForeachPtr(Job *, job, group->job_list) if (job->done.Get() == 0) { done = false; break; } } if (!blocking) return done; Thread::Switch(); } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool JobManager::ExecutePendingJob(uint worker_id) { // Look for a job to execute. Job *job = NULL; { #if __USE_LOCK_FREE_JOB_QUEUE__ pending_queue.dequeue(job); #else nMutexLock pending_lock(pending_queue_mutex); if (pending_queue.GetCount() > 0) { job = pending_queue.Top(); pending_queue.Pop(); } #endif } // Execute job. if (job) { #if __ENABLE_ITT_API__ __itt_task_begin(domain, __itt_null, __itt_null, __itt_string_handle_create(job->name)); #endif // job->time_start = Platform::Get().GetTime(); job->Execute(worker_id); job->done.Set(1); // job->time_end = Platform::Get().GetTime(); Thread::Switch(); // let other workers do their job #if __ENABLE_ITT_API__ __itt_task_end(domain); #endif } return asbool(job); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint JobManager::GetWorkerPoolSize() const { return pool.GetCount(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ JobManager::JobManager() : pending_queue(256) { #if __ENABLE_ITT_API__ domain = __itt_domain_create("GS.JobManager"); #endif #if (__USE_LOCK_FREE_JOB_QUEUE__ == 0) pending_queue_mutex = new nMutex; #endif } JobManager::~JobManager() { FreeJobThreadPool(); } JobGroup::JobGroup() { job_list_mutex = new Mutex; } //------------------------------------------------------------------------------