/** * Callback that is called from the busy worker thread when it exists. * @param status Exit status. */ static void busyWorkerTerminated(systhreadexitstatus_t status) { DENG_ASSERT(BusyMode_Active()); if(status == DENG_THREAD_STOPPED_WITH_EXCEPTION) { _api_Busy.WorkerError("Uncaught exception from busy thread."); } }
dd_bool BusyMode_IsWorkerThread(uint threadId) { dd_bool result; if(!BusyMode_Active() || !busyThread) return false; /// @todo Is locking necessary? Sys_Lock(busy_Mutex); result = (Sys_ThreadId(busyThread) == threadId); Sys_Unlock(busy_Mutex); return result; }
DENG_EXTERN_C void Sys_Quit(void) { if(BusyMode_Active()) { // The busy worker is running; we cannot just stop it abruptly. Sys_MessageBox2(MBT_WARNING, DOOMSDAY_NICENAME, "Cannot quit while in busy mode.", "Try again later after the current operation has finished.", 0); return; } appShutdown = true; // It's time to stop the main loop. DENG2_APP->stopLoop(DD_GameLoopExitCode()); }
/** * The busy loop callback function. Called periodically in the main (UI) thread * while the busy worker is running. */ void BusyMode_Loop(void) { if(!busyTask || !BusyMode_Active()) return; dd_bool canUpload = !(busyTask->mode & BUSYF_NO_UPLOADS); timespan_t oldTime; // Post and discard all input events. DD_ProcessEvents(0); DD_ProcessSharpEvents(0); if(canUpload) { ClientWindowSystem::main().glActivate(); // Any deferred content needs to get uploaded. GL_ProcessDeferredTasks(15); } // We accumulate time in the busy loop so that the animation of a task // sequence doesn't jump around but remains continuous. oldTime = busyTime; busyTime = Timer_RealSeconds() - busyTask->_startTime; if(busyTime > oldTime) { accumulatedBusyTime += busyTime - oldTime; } Sys_Lock(busy_Mutex); busyDoneCopy = busyDone; Sys_Unlock(busy_Mutex); if(!busyDoneCopy || (canUpload && GL_DeferredTaskCount() > 0) || !Con_IsProgressAnimationCompleted()) { // Let's keep running the busy loop. ClientWindowSystem::main().draw(); return; } // Stop the loop. BusyMode_Exit(); }
timespan_t DD_LatestRunTicsStartTime(void) { if(BusyMode_Active()) return Timer_Seconds(); return lastRunTicsTime; }
boolean DD_IsFrameTimeAdvancing(void) { if(BusyMode_Active()) return false; return tickFrame || netGame; }
timespan_t BusyMode_ElapsedTime(void) { if(!BusyMode_Active()) return 0; return accumulatedBusyTime; }
int BusyMode_RunTasks(BusyTask* tasks, int numTasks) { const char* currentTaskName = NULL; BusyTask* task; int i, mode; int result = 0; if(BusyMode_Active()) { App_Error("BusyMode: Internal error, already busy..."); exit(1); // Unreachable. } if(!tasks || numTasks <= 0) return result; // Hmm, no work? // Pick the first task. task = tasks; int initialMode = task->mode; preBusySetup(initialMode); // Process tasks. for(i = 0; i < numTasks; ++i, task++) { // If no new task name is specified, continue using the name of the previous task. if(task->name) { if(task->name[0]) currentTaskName = task->name; else // Clear the name. currentTaskName = NULL; } mode = task->mode; /// @todo Kludge: Force BUSYF_STARTUP here so that the animation of one task /// is not drawn on top of the last frame of the previous. if(numTasks > 1) { mode |= BUSYF_STARTUP; } // kludge end // Null tasks are not processed (implicit success). if(!task->worker) continue; /** * Process the work. */ #ifdef __CLIENT__ // Is the worker updating its progress? if(task->maxProgress > 0) Con_InitProgress2(task->maxProgress, task->progressStart, task->progressEnd); #endif // Invoke the worker in a new thread. /// @todo Kludge: Presently a temporary local task is needed so that we can modify /// the task name and mode flags. { BusyTask* tmp = newTask(mode, task->worker, task->workerData, currentTaskName); result = runTask(tmp); // We are now done with this task. deleteTask(tmp); if(result) break; } // kludge end. } postBusyCleanup(); return result; }
BusyTask* BusyMode_CurrentTask(void) { if(!BusyMode_Active()) return NULL; return busyTask; }