void TaskSchedular::AsyncWork(TaskRunner* runner) { Task* toExec = nullptr; TaskRunner::AsyncGetRunner() = runner; runner->id = std::this_thread::get_id(); while (toExec || (toExec = AsyncGetWork(runner))) { #ifdef NEX_TASK_SCHEDULAR_CHECKS String lastTask = toExec->_name; if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Worker: " << runner->_name << ", running task: " << lastTask; Trace(str.str()); } #endif toExec = TaskRunner::Execute(toExec); #ifdef NEX_TASK_SCHEDULAR_CHECKS if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Worker: " << runner->_name << ", finished task: " << lastTask; Trace(str.str()); } #endif } // reliquish all tasks to someone else runner->Relinquish(this); #ifdef NEX_TASK_SCHEDULAR_CHECKS if (TaskSchedular::_doAllowTraces) { Trace(runner->_name + " finished."); } #endif }
TaskSchedular::TaskSchedular() { quit.store(false, std::memory_order_relaxed); unsigned int c = std::max<uint32>(1, std::thread::hardware_concurrency()-1); // @todo might kill performance, we need a way to serialize // in case we dont have the hardware OutStringStream str; str << "Worker Count: " << c; Trace(str.str()); runners.reserve(c); for(uint32 i = 0; i < c; ++i) { TaskRunner* r = NEX_NEW(TaskRunner(i+1)); #ifdef NEX_TASK_SCHEDULAR_CHECKS if (TaskSchedular::_doAllowTraces) { OutStringStream str2; str2 << "Runner" << i; r->_name = str2.str(); } #endif runners.push_back(r); } mainThread = NEX_NEW(TaskRunner(c+1)); mainThread->MakeMain(); runners.push_back(mainThread); #ifdef NEX_TASK_SCHEDULAR_CHECKS mainThread->_name = "Main"; #endif for(uint32 i = 0; i < runners.size()-1; ++i) { runners[i]->Start(this); } }
void TaskSchedular::AsyncResume(Task* waitingOn, uint32 limit) { Task* toExec = nullptr; TaskRunner* runner = TaskRunner::AsyncGetRunner(); while(waitingOn->meta.refCount.load(std::memory_order_relaxed) > (int)limit) { if (toExec || (toExec = AsyncGetWork(runner, false)) ) { #ifdef NEX_TASK_SCHEDULAR_CHECKS if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Waited: " << runner->_name << ", running task: " << toExec->_name; Trace(str.str()); } #endif toExec = TaskRunner::Execute(toExec); } } if (toExec) { #ifdef NEX_TASK_SCHEDULAR_CHECKS NEX_ASSERT(toExec->meta.state == Task::TASK_QUEUED || toExec->meta.state == Task::TASK_INIT); toExec->meta.state = Task::TASK_QUEUED; #endif runner->AddTask(toExec); } }
void TaskSchedular::AsyncSubmit(Task* singleTask) { singleTask->PrepareSubmit(); std::lock_guard<mutex_type> guard(lock); #ifdef NEX_TASK_SCHEDULAR_CHECKS NEX_ASSERT(singleTask->meta.state == Task::TASK_QUEUED || singleTask->meta.state == Task::TASK_INIT); singleTask->meta.state = Task::TASK_QUEUED; if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Submit to queue: " << "task: " << singleTask->_name; Trace(str.str()); } #endif submittedTasks.push_back(singleTask); }
void TaskSchedular::AsyncAddChildTask(Task* task) { NEX_ASSERT(task); task->PrepareSubmit(); TaskRunner* runner = TaskRunner::AsyncGetRunner(); #ifdef NEX_TASK_SCHEDULAR_CHECKS if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Thread: " << runner->_name << ", task queue: " << runner->requestQueue.Tail() << " via, " << task->_name; Trace(str.str()); } #endif runner->AddTask(task); #ifdef NEX_TASK_SCHEDULAR_CHECKS task->meta.state = Task::TASK_QUEUED; #endif runner->taskCount++; }
void TaskSchedular::AsyncSubmit(Task** multipleTasks, uint32 n) { std::lock_guard<mutex_type> guard(lock); #ifdef NEX_TASK_SCHEDULAR_CHECKS if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Submit: tasks " << n; Trace(str.str()); } std::for_each(multipleTasks, multipleTasks+n, [] (Task* singleTask) { NEX_ASSERT(singleTask->meta.state == Task::TASK_QUEUED || singleTask->meta.state == Task::TASK_INIT); singleTask->meta.state = Task::TASK_QUEUED; }); #endif std::for_each(multipleTasks, multipleTasks + n, [](Task* singleTask) { singleTask->PrepareSubmit(); }); submittedTasks.insert(submittedTasks.end(), multipleTasks, multipleTasks+n); }
Task* TaskSchedular::AsyncGetWork(TaskRunner* runner, bool repeat) { // try to retrieve from own queue first Task* task = nullptr; do { task = runner->RetrieveTask(); if (!task) { uint32 n = (uint32)runners.size(); uint32 i = runner->laststeal; uint32 counter = 0; TaskRunner* neighbour; while( !task && (counter++) < n ) { neighbour = runners[(i++)%n]; if (neighbour == runner) continue; task = neighbour->requestQueue.AsyncSteal(); } #ifdef NEX_TASK_SCHEDULAR_CHECKS if (task) { if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Stolen: " << runner->_name << " has stolen " << neighbour->_name << "'s task: " << task->_name; Trace(str.str()); } runner->laststeal = i; } #endif } if (!task) { // try to work on a submitted task if(lock.try_lock()) { if (submittedTasks.size()) { task = submittedTasks.back(); submittedTasks.pop_back(); #ifdef NEX_TASK_SCHEDULAR_CHECKS if (task) { if (TaskSchedular::_doAllowTraces) { OutStringStream str; str << "Dequed: " << runner->_name << " has fetched task: " << task->_name; Trace(str.str()); } } #endif } lock.unlock(); } } if (!repeat || task) { #ifdef NEX_TASK_SCHEDULAR_CHECKS // This check is problematic, as a task may be queued once, // Run is completed, by the time PrepareSubmit is called on it, // the task may not have been marked as finished and it might // be marked so afterwards, thus the state might come up as // either Running or Finished here. if(task) NEX_ASSERT(task->meta.state == Task::TASK_QUEUED); #endif return task; } #ifdef NEX_TASK_SCHEDULAR_CHECKS if (TaskSchedular::_doAllowTraces) { //OutStringStream str; //str << "Yieldx: " << runner->_name << " is yielding."; //Trace(str.str()); } #endif std::this_thread::yield(); } while(!quit.load(std::memory_order_relaxed)); return nullptr; }
void Simulation::draw(render::Context& context) { context.setStencilBuffer(getWorldSize().getWidth().value(), getWorldSize().getHeight().value()); // Render modules m_modules.draw(*this, context); // Draw objects for (auto& obj : m_objects) { Assert(obj); if (obj->isVisible()) obj->draw(context); } #if defined(CECE_ENABLE_RENDER) && defined(CECE_ENABLE_BOX2D_PHYSICS) && defined(CECE_ENABLE_BOX2D_PHYSICS_DEBUG) if (isDrawPhysics()) m_world.DrawDebugData(); #endif #if CONFIG_RENDER_TEXT_ENABLE context.disableStencilBuffer(); if (isSimulationTimeRender()) { if (!m_font) { m_font.create(context, g_fontData); m_font->setSize(getFontSize()); } OutStringStream oss; { auto time = getTotalTime().value(); unsigned int seconds = time; unsigned int milliseconds = static_cast<unsigned int>(time * 1000) % 1000; const unsigned int hours = seconds / (60 * 60); seconds %= (60 * 60); const unsigned int minutes = seconds / 60; seconds %= 60; if (hours) { oss << std::setw(2) << std::setfill('0') << hours << ":"; oss << std::setw(2) << std::setfill('0') << minutes << ":"; } else if (minutes) { oss << std::setw(2) << std::setfill('0') << minutes << ":"; } oss << std::setw(2) << std::setfill('0') << seconds << "."; oss << std::setw(3) << std::setfill('0') << milliseconds; } if (hasUnlimitedIterations()) { oss << " (" << getIteration() << " / -)"; } else { oss << " (" << getIteration() << " / " << getIterations() << ")"; } m_font->draw(context, oss.str(), getFontColor()); } #endif }