|
|
@@ -22,31 +22,21 @@
|
|
|
#include "pStatClient.h"
|
|
|
#include "pStatTimer.h"
|
|
|
#include "clockObject.h"
|
|
|
+#include "config_event.h"
|
|
|
#include <algorithm>
|
|
|
|
|
|
TypeHandle AsyncTaskManager::_type_handle;
|
|
|
|
|
|
-PStatCollector AsyncTaskManager::_task_pcollector("Task");
|
|
|
-PStatCollector AsyncTaskManager::_wait_pcollector("Wait");
|
|
|
-
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: AsyncTaskManager::Constructor
|
|
|
// Access: Published
|
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
AsyncTaskManager::
|
|
|
-AsyncTaskManager(const string &name, int num_threads) :
|
|
|
+AsyncTaskManager(const string &name) :
|
|
|
Namable(name),
|
|
|
- _num_threads(0),
|
|
|
- _cvar(_lock),
|
|
|
- _num_tasks(0),
|
|
|
- _num_busy_threads(0),
|
|
|
- _state(S_initial),
|
|
|
- _current_sort(INT_MAX),
|
|
|
- _clock(ClockObject::get_global_clock()),
|
|
|
- _tick_clock(false)
|
|
|
+ _clock(ClockObject::get_global_clock())
|
|
|
{
|
|
|
- set_num_threads(num_threads);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -56,75 +46,100 @@ AsyncTaskManager(const string &name, int num_threads) :
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
AsyncTaskManager::
|
|
|
~AsyncTaskManager() {
|
|
|
- stop_threads();
|
|
|
+ MutexHolder holder(_lock);
|
|
|
|
|
|
- TaskHeap::const_iterator ti;
|
|
|
- for (ti = _active.begin(); ti != _active.end(); ++ti) {
|
|
|
- AsyncTask *task = (*ti);
|
|
|
- cleanup_task(task, false);
|
|
|
- }
|
|
|
- for (ti = _next_active.begin(); ti != _next_active.end(); ++ti) {
|
|
|
- AsyncTask *task = (*ti);
|
|
|
- cleanup_task(task, false);
|
|
|
- }
|
|
|
- for (ti = _sleeping.begin(); ti != _sleeping.end(); ++ti) {
|
|
|
- AsyncTask *task = (*ti);
|
|
|
- cleanup_task(task, false);
|
|
|
+ TaskChains::iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ chain->do_cleanup();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::set_num_threads
|
|
|
+// Function: AsyncTaskManager::get_num_task_chains
|
|
|
// Access: Published
|
|
|
-// Description: Changes the number of threads for this task manager.
|
|
|
-// This may require stopping the threads if they are
|
|
|
-// already running.
|
|
|
+// Description: Returns the number of different task chains.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-set_num_threads(int num_threads) {
|
|
|
- nassertv(num_threads >= 0);
|
|
|
-
|
|
|
- if (!Thread::is_threading_supported()) {
|
|
|
- num_threads = 0;
|
|
|
- }
|
|
|
-
|
|
|
+int AsyncTaskManager::
|
|
|
+get_num_task_chains() const {
|
|
|
MutexHolder holder(_lock);
|
|
|
- if (_num_threads != num_threads) {
|
|
|
- do_stop_threads();
|
|
|
- _num_threads = num_threads;
|
|
|
- }
|
|
|
+ return _task_chains.size();
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::stop_threads
|
|
|
+// Function: AsyncTaskManager::get_task_chain
|
|
|
// Access: Published
|
|
|
-// Description: Stops any threads that are currently running. If any
|
|
|
-// tasks are still pending and have not yet been picked
|
|
|
-// up by a thread, they will not be serviced unless
|
|
|
-// poll() or start_threads() is later called.
|
|
|
+// Description: Returns the nth task chain.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-stop_threads() {
|
|
|
- if (_state == S_started || _state == S_aborting) {
|
|
|
- // Clean up all of the threads.
|
|
|
- MutexHolder holder(_lock);
|
|
|
- do_stop_threads();
|
|
|
- }
|
|
|
+AsyncTaskChain *AsyncTaskManager::
|
|
|
+get_task_chain(int n) const {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
+ nassertr(n >= 0 && n < (int)_task_chains.size(), NULL);
|
|
|
+ return _task_chains[n];
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::start_threads
|
|
|
+// Function: AsyncTaskManager::make_task_chain
|
|
|
// Access: Published
|
|
|
-// Description: Starts any requested threads to service the tasks on
|
|
|
-// the queue. This is normally not necessary, since
|
|
|
-// adding a task will start the threads automatically.
|
|
|
+// Description: Creates a new AsyncTaskChain of the indicated name
|
|
|
+// and stores it within the AsyncTaskManager. If a task
|
|
|
+// chain with this name already exists, returns it
|
|
|
+// instead.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-start_threads() {
|
|
|
- if (_state == S_initial || _state == S_aborting) {
|
|
|
- MutexHolder holder(_lock);
|
|
|
- do_start_threads();
|
|
|
+AsyncTaskChain *AsyncTaskManager::
|
|
|
+make_task_chain(const string &name) {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
+ return do_make_task_chain(name);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AsyncTaskManager::find_task_chain
|
|
|
+// Access: Protected
|
|
|
+// Description: Searches a new AsyncTaskChain of the indicated name
|
|
|
+// and returns it if it exists, or NULL otherwise.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+AsyncTaskChain *AsyncTaskManager::
|
|
|
+find_task_chain(const string &name) {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
+ return do_find_task_chain(name);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AsyncTaskManager::remove_task_chain
|
|
|
+// Access: Protected
|
|
|
+// Description: Removes the AsyncTaskChain of the indicated name.
|
|
|
+// If the chain still has tasks, this will block until
|
|
|
+// all tasks are finished.
|
|
|
+//
|
|
|
+// Returns true if successful, or false if the chain did
|
|
|
+// not exist.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+bool AsyncTaskManager::
|
|
|
+remove_task_chain(const string &name) {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
+
|
|
|
+ PT(AsyncTaskChain) chain = new AsyncTaskChain(this, name);
|
|
|
+ TaskChains::iterator tci = _task_chains.find(chain);
|
|
|
+ if (tci == _task_chains.end()) {
|
|
|
+ // No chain.
|
|
|
+ return false;
|
|
|
}
|
|
|
+
|
|
|
+ chain = (*tci);
|
|
|
+
|
|
|
+ while (chain->_num_tasks != 0) {
|
|
|
+ // Still has tasks.
|
|
|
+ event_cat.info()
|
|
|
+ << "Waiting for tasks on chain " << name << " to finish.\n";
|
|
|
+ chain->do_wait_for_tasks();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Safe to remove.
|
|
|
+ chain->do_cleanup();
|
|
|
+ _task_chains.erase(tci);
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -151,35 +166,16 @@ add(AsyncTask *task) {
|
|
|
task->_state == AsyncTask::S_inactive);
|
|
|
nassertv(!do_has_task(task));
|
|
|
|
|
|
- do_start_threads();
|
|
|
-
|
|
|
- task->_manager = this;
|
|
|
add_task_by_name(task);
|
|
|
|
|
|
- double now = _clock->get_frame_time();
|
|
|
- task->_start_time = now;
|
|
|
-
|
|
|
- if (task->has_delay()) {
|
|
|
- // This is a deferred task. Add it to the sleeping queue.
|
|
|
- task->_wake_time = now + task->get_delay();
|
|
|
- task->_state = AsyncTask::S_sleeping;
|
|
|
- _sleeping.push_back(task);
|
|
|
- push_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
|
|
|
-
|
|
|
- } else {
|
|
|
- // This is an active task. Add it to the active set.
|
|
|
- task->_state = AsyncTask::S_active;
|
|
|
- if (task->get_sort() > _current_sort) {
|
|
|
- // It will run this frame.
|
|
|
- _active.push_back(task);
|
|
|
- push_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
|
|
|
- } else {
|
|
|
- // It will run next frame.
|
|
|
- _next_active.push_back(task);
|
|
|
- }
|
|
|
+ AsyncTaskChain *chain = do_find_task_chain(task->_chain_name);
|
|
|
+ if (chain == (AsyncTaskChain *)NULL) {
|
|
|
+ event_cat.warning()
|
|
|
+ << "Creating implicit AsyncTaskChain " << task->_chain_name
|
|
|
+ << " for " << get_type() << " " << get_name() << "\n";
|
|
|
+ chain = do_make_task_chain(task->_chain_name);
|
|
|
}
|
|
|
- ++_num_tasks;
|
|
|
- _cvar.signal_all();
|
|
|
+ chain->do_add(task);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -311,43 +307,9 @@ remove(const AsyncTaskCollection &tasks) {
|
|
|
// Not a member of this manager, or already removed.
|
|
|
nassertr(!do_has_task(task), num_removed);
|
|
|
} else {
|
|
|
- switch (task->_state) {
|
|
|
- case AsyncTask::S_servicing:
|
|
|
- // This task is being serviced.
|
|
|
- task->_state = AsyncTask::S_servicing_removed;
|
|
|
- break;
|
|
|
-
|
|
|
- case AsyncTask::S_servicing_removed:
|
|
|
- // Being serviced, though it will be removed later.
|
|
|
- break;
|
|
|
-
|
|
|
- case AsyncTask::S_sleeping:
|
|
|
- // Sleeping, easy.
|
|
|
- {
|
|
|
- int index = find_task_on_heap(_sleeping, task);
|
|
|
- nassertr(index != -1, num_removed);
|
|
|
- _sleeping.erase(_sleeping.begin() + index);
|
|
|
- make_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
|
|
|
- ++num_removed;
|
|
|
- cleanup_task(task, false);
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case AsyncTask::S_active:
|
|
|
- {
|
|
|
- // Active, but not being serviced, easy.
|
|
|
- int index = find_task_on_heap(_active, task);
|
|
|
- if (index != -1) {
|
|
|
- _active.erase(_active.begin() + index);
|
|
|
- make_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
|
|
|
- } else {
|
|
|
- index = find_task_on_heap(_next_active, task);
|
|
|
- nassertr(index != -1, num_removed);
|
|
|
- _next_active.erase(_next_active.begin() + index);
|
|
|
- }
|
|
|
- ++num_removed;
|
|
|
- cleanup_task(task, false);
|
|
|
- }
|
|
|
+ nassertr(task->_chain->_manager == this, num_removed);
|
|
|
+ if (task->_chain->do_remove(task)) {
|
|
|
+ ++num_removed;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -364,27 +326,56 @@ void AsyncTaskManager::
|
|
|
wait_for_tasks() {
|
|
|
MutexHolder holder(_lock);
|
|
|
|
|
|
- do_start_threads();
|
|
|
-
|
|
|
- if (_threads.empty()) {
|
|
|
- // Non-threaded case.
|
|
|
- while (_num_tasks > 0) {
|
|
|
- if (_state == S_shutdown || _state == S_aborting) {
|
|
|
- return;
|
|
|
- }
|
|
|
- do_poll();
|
|
|
+ // Wait for each of our task chains to finish.
|
|
|
+ while (_num_tasks > 0) {
|
|
|
+ TaskChains::iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ chain->do_wait_for_tasks();
|
|
|
}
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- } else {
|
|
|
- // Threaded case.
|
|
|
- while (_num_tasks > 0) {
|
|
|
- if (_state == S_shutdown || _state == S_aborting) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- PStatTimer timer(_wait_pcollector);
|
|
|
- _cvar.wait();
|
|
|
- }
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AsyncTaskManager::stop_threads
|
|
|
+// Access: Published
|
|
|
+// Description: Stops any threads that are currently running. If any
|
|
|
+// tasks are still pending and have not yet been picked
|
|
|
+// up by a thread, they will not be serviced unless
|
|
|
+// poll() or start_threads() is later called.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AsyncTaskManager::
|
|
|
+stop_threads() {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
+
|
|
|
+ TaskChains::iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ chain->do_stop_threads();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AsyncTaskManager::start_threads
|
|
|
+// Access: Published
|
|
|
+// Description: Starts any requested threads to service the tasks on
|
|
|
+// the queue. This is normally not necessary, since
|
|
|
+// adding a task will start the threads automatically.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void AsyncTaskManager::
|
|
|
+start_threads() {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
+
|
|
|
+ TaskChains::iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ chain->do_start_threads();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -395,9 +386,19 @@ wait_for_tasks() {
|
|
|
// on the task manager, at the time of the call.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
AsyncTaskCollection AsyncTaskManager::
|
|
|
-get_tasks() {
|
|
|
- AsyncTaskCollection result = get_active_tasks();
|
|
|
- result.add_tasks_from(get_sleeping_tasks());
|
|
|
+get_tasks() const {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
+
|
|
|
+ AsyncTaskCollection result;
|
|
|
+ TaskChains::const_iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ result.add_tasks_from(chain->do_get_active_tasks());
|
|
|
+ result.add_tasks_from(chain->do_get_sleeping_tasks());
|
|
|
+ }
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
@@ -409,24 +410,16 @@ get_tasks() {
|
|
|
// call.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
AsyncTaskCollection AsyncTaskManager::
|
|
|
-get_active_tasks() {
|
|
|
- AsyncTaskCollection result;
|
|
|
+get_active_tasks() const {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
|
|
|
- Threads::const_iterator thi;
|
|
|
- for (thi = _threads.begin(); thi != _threads.end(); ++thi) {
|
|
|
- AsyncTask *task = (*thi)->_servicing;
|
|
|
- if (task != (AsyncTask *)NULL) {
|
|
|
- result.add_task(task);
|
|
|
- }
|
|
|
- }
|
|
|
- TaskHeap::const_iterator ti;
|
|
|
- for (ti = _active.begin(); ti != _active.end(); ++ti) {
|
|
|
- AsyncTask *task = (*ti);
|
|
|
- result.add_task(task);
|
|
|
- }
|
|
|
- for (ti = _next_active.begin(); ti != _next_active.end(); ++ti) {
|
|
|
- AsyncTask *task = (*ti);
|
|
|
- result.add_task(task);
|
|
|
+ AsyncTaskCollection result;
|
|
|
+ TaskChains::const_iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ result.add_tasks_from(chain->do_get_active_tasks());
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
@@ -440,13 +433,16 @@ get_active_tasks() {
|
|
|
// call.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
AsyncTaskCollection AsyncTaskManager::
|
|
|
-get_sleeping_tasks() {
|
|
|
- AsyncTaskCollection result;
|
|
|
+get_sleeping_tasks() const {
|
|
|
+ MutexHolder holder(_lock);
|
|
|
|
|
|
- TaskHeap::const_iterator ti;
|
|
|
- for (ti = _sleeping.begin(); ti != _sleeping.end(); ++ti) {
|
|
|
- AsyncTask *task = (*ti);
|
|
|
- result.add_task(task);
|
|
|
+ AsyncTaskCollection result;
|
|
|
+ TaskChains::const_iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ result.add_tasks_from(chain->do_get_sleeping_tasks());
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
@@ -464,13 +460,14 @@ get_sleeping_tasks() {
|
|
|
void AsyncTaskManager::
|
|
|
poll() {
|
|
|
MutexHolder holder(_lock);
|
|
|
- do_start_threads();
|
|
|
|
|
|
- if (!_threads.empty()) {
|
|
|
- return;
|
|
|
+ TaskChains::iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ chain->do_poll();
|
|
|
}
|
|
|
-
|
|
|
- do_poll();
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -497,66 +494,12 @@ write(ostream &out, int indent_level) const {
|
|
|
indent(out, indent_level)
|
|
|
<< get_type() << " " << get_name() << "\n";
|
|
|
|
|
|
- // Collect a list of all active tasks, then sort them into order for
|
|
|
- // output.
|
|
|
- TaskHeap tasks = _active;
|
|
|
- tasks.insert(tasks.end(), _next_active.begin(), _next_active.end());
|
|
|
-
|
|
|
- Threads::const_iterator thi;
|
|
|
- for (thi = _threads.begin(); thi != _threads.end(); ++thi) {
|
|
|
- AsyncTask *task = (*thi)->_servicing;
|
|
|
- if (task != (AsyncTask *)NULL &&
|
|
|
- task->_state != AsyncTask::S_servicing_removed) {
|
|
|
- tasks.push_back(task);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!tasks.empty()) {
|
|
|
- indent(out, indent_level + 2)
|
|
|
- << "Active tasks:\n";
|
|
|
-
|
|
|
- sort(tasks.begin(), tasks.end(), AsyncTaskSortPriority());
|
|
|
-
|
|
|
- // Since AsyncTaskSortPriority() sorts backwards (because of STL's
|
|
|
- // push_heap semantics), we go through the task list in reverse
|
|
|
- // order to print them forwards.
|
|
|
- TaskHeap::reverse_iterator ti;
|
|
|
- int current_sort = tasks.back()->get_sort() - 1;
|
|
|
- for (ti = tasks.rbegin(); ti != tasks.rend(); ++ti) {
|
|
|
- AsyncTask *task = (*ti);
|
|
|
- if (task->get_sort() != current_sort) {
|
|
|
- current_sort = task->get_sort();
|
|
|
- indent(out, indent_level + 2)
|
|
|
- << "sort = " << current_sort << "\n";
|
|
|
- }
|
|
|
- if (task->_state == AsyncTask::S_servicing) {
|
|
|
- indent(out, indent_level + 3)
|
|
|
- << "*" << *task << "\n";
|
|
|
- } else {
|
|
|
- indent(out, indent_level + 4)
|
|
|
- << *task << "\n";
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!_sleeping.empty()) {
|
|
|
- indent(out, indent_level + 2)
|
|
|
- << "Sleeping tasks:\n";
|
|
|
- double now = _clock->get_frame_time();
|
|
|
-
|
|
|
- // Instead of iterating through the _sleeping list in heap order,
|
|
|
- // copy it and then use repeated pops to get it out in sorted
|
|
|
- // order, for the user's satisfaction.
|
|
|
- TaskHeap sleeping = _sleeping;
|
|
|
- while (!sleeping.empty()) {
|
|
|
- PT(AsyncTask) task = sleeping.front();
|
|
|
- pop_heap(sleeping.begin(), sleeping.end(), AsyncTaskSortWakeTime());
|
|
|
- sleeping.pop_back();
|
|
|
-
|
|
|
- indent(out, indent_level + 4)
|
|
|
- << task->get_wake_time() - now << "s: "
|
|
|
- << *task << "\n";
|
|
|
- }
|
|
|
+ TaskChains::const_iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ chain->do_write(out, indent_level + 2);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -570,29 +513,55 @@ write(ostream &out, int indent_level) const {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
bool AsyncTaskManager::
|
|
|
do_has_task(AsyncTask *task) const {
|
|
|
- return (find_task_on_heap(_active, task) != -1 ||
|
|
|
- find_task_on_heap(_next_active, task) != -1 ||
|
|
|
- find_task_on_heap(_sleeping, task) != -1);
|
|
|
+ TaskChains::const_iterator tci;
|
|
|
+ for (tci = _task_chains.begin();
|
|
|
+ tci != _task_chains.end();
|
|
|
+ ++tci) {
|
|
|
+ AsyncTaskChain *chain = (*tci);
|
|
|
+ if (chain->do_has_task(task)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::find_task_on_heap
|
|
|
+// Function: AsyncTaskManager::do_make_task_chain
|
|
|
// Access: Protected
|
|
|
-// Description: Returns the index number of the indicated task within
|
|
|
-// the specified task list, or -1 if the task is not
|
|
|
-// found in the list (this may mean that it is currently
|
|
|
-// being serviced). Assumes that the lock is currently
|
|
|
-// held.
|
|
|
+// Description: Creates a new AsyncTaskChain of the indicated name
|
|
|
+// and stores it within the AsyncTaskManager. If a task
|
|
|
+// chain with this name already exists, returns it
|
|
|
+// instead.
|
|
|
+//
|
|
|
+// Assumes the lock is held.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-int AsyncTaskManager::
|
|
|
-find_task_on_heap(const TaskHeap &heap, AsyncTask *task) const {
|
|
|
- for (int i = 0; i < (int)heap.size(); ++i) {
|
|
|
- if (heap[i] == task) {
|
|
|
- return i;
|
|
|
- }
|
|
|
+AsyncTaskChain *AsyncTaskManager::
|
|
|
+do_make_task_chain(const string &name) {
|
|
|
+ PT(AsyncTaskChain) chain = new AsyncTaskChain(this, name);
|
|
|
+
|
|
|
+ TaskChains::const_iterator tci = _task_chains.insert(chain).first;
|
|
|
+ return (*tci);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: AsyncTaskManager::do_find_task_chain
|
|
|
+// Access: Protected
|
|
|
+// Description: Searches a new AsyncTaskChain of the indicated name
|
|
|
+// and returns it if it exists, or NULL otherwise.
|
|
|
+//
|
|
|
+// Assumes the lock is held.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+AsyncTaskChain *AsyncTaskManager::
|
|
|
+do_find_task_chain(const string &name) {
|
|
|
+ PT(AsyncTaskChain) chain = new AsyncTaskChain(this, name);
|
|
|
+
|
|
|
+ TaskChains::const_iterator tci = _task_chains.find(chain);
|
|
|
+ if (tci != _task_chains.end()) {
|
|
|
+ return (*tci);
|
|
|
}
|
|
|
|
|
|
- return -1;
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -624,307 +593,3 @@ remove_task_by_name(AsyncTask *task) {
|
|
|
nassertv(false);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::service_one_task
|
|
|
-// Access: Protected
|
|
|
-// Description: Pops a single task off the active queue, services it,
|
|
|
-// and restores it to the end of the queue. This is
|
|
|
-// called internally only within one of the task
|
|
|
-// threads. Assumes the lock is already held.
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-service_one_task(AsyncTaskManager::AsyncTaskManagerThread *thread) {
|
|
|
- if (!_active.empty()) {
|
|
|
- PT(AsyncTask) task = _active.front();
|
|
|
- pop_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
|
|
|
- _active.pop_back();
|
|
|
-
|
|
|
- if (thread != (AsyncTaskManager::AsyncTaskManagerThread *)NULL) {
|
|
|
- thread->_servicing = task;
|
|
|
- }
|
|
|
-
|
|
|
- nassertv(task->get_sort() == _current_sort);
|
|
|
- nassertv(task->_state == AsyncTask::S_active);
|
|
|
- task->_state = AsyncTask::S_servicing;
|
|
|
- task->_servicing_thread = thread;
|
|
|
-
|
|
|
- // Now release the manager lock while we actually service the
|
|
|
- // task.
|
|
|
- _lock.release();
|
|
|
- AsyncTask::DoneStatus ds = task->do_task();
|
|
|
-
|
|
|
- // Now we have to re-acquire the manager lock, so we can put the
|
|
|
- // task back on the queue (and so we can return with the lock
|
|
|
- // still held).
|
|
|
- _lock.lock();
|
|
|
-
|
|
|
- if (thread != (AsyncTaskManager::AsyncTaskManagerThread *)NULL) {
|
|
|
- thread->_servicing = NULL;
|
|
|
- }
|
|
|
- task->_servicing_thread = NULL;
|
|
|
-
|
|
|
- if (task->_manager == this) {
|
|
|
- if (task->_state == AsyncTask::S_servicing_removed) {
|
|
|
- // This task wants to kill itself.
|
|
|
- cleanup_task(task, false);
|
|
|
-
|
|
|
- } else {
|
|
|
- switch (ds) {
|
|
|
- case AsyncTask::DS_cont:
|
|
|
- // The task is still alive; put it on the next frame's active
|
|
|
- // queue.
|
|
|
- task->_state = AsyncTask::S_active;
|
|
|
- _next_active.push_back(task);
|
|
|
- _cvar.signal_all();
|
|
|
- break;
|
|
|
-
|
|
|
- case AsyncTask::DS_again:
|
|
|
- // The task wants to sleep again.
|
|
|
- {
|
|
|
- double now = _clock->get_frame_time();
|
|
|
- task->_wake_time = now + task->get_delay();
|
|
|
- task->_state = AsyncTask::S_sleeping;
|
|
|
- _sleeping.push_back(task);
|
|
|
- push_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
|
|
|
- _cvar.signal_all();
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case AsyncTask::DS_abort:
|
|
|
- // The task had an exception and wants to raise a big flag.
|
|
|
- cleanup_task(task, false);
|
|
|
- if (_state == S_started) {
|
|
|
- _state = S_aborting;
|
|
|
- _cvar.signal_all();
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- // The task has finished.
|
|
|
- cleanup_task(task, true);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::cleanup_task
|
|
|
-// Access: Protected
|
|
|
-// Description: Called internally when a task has completed (or been
|
|
|
-// interrupted) and is about to be removed from the
|
|
|
-// active queue. Assumes the lock is held.
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-cleanup_task(AsyncTask *task, bool clean_exit) {
|
|
|
- nassertv(task->_manager == this);
|
|
|
-
|
|
|
- task->_state = AsyncTask::S_inactive;
|
|
|
- task->_manager = NULL;
|
|
|
- --_num_tasks;
|
|
|
-
|
|
|
- remove_task_by_name(task);
|
|
|
-
|
|
|
- if (clean_exit && !task->_done_event.empty()) {
|
|
|
- PT_Event event = new Event(task->_done_event);
|
|
|
- event->add_parameter(EventParameter(task));
|
|
|
- throw_event(event);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::finish_sort_group
|
|
|
-// Access: Protected
|
|
|
-// Description: Called internally when all tasks of a given sort
|
|
|
-// value have been completed, and it is time to
|
|
|
-// increment to the next sort value, or begin the next
|
|
|
-// epoch. Assumes the lock is held.
|
|
|
-//
|
|
|
-// Returns true if there are more tasks on the queue
|
|
|
-// after this operation, or false if the task list is
|
|
|
-// empty and we need to wait.
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-bool AsyncTaskManager::
|
|
|
-finish_sort_group() {
|
|
|
- nassertr(_num_busy_threads == 0, true);
|
|
|
-
|
|
|
- if (!_active.empty()) {
|
|
|
- // There are more tasks; just set the next sort value.
|
|
|
- nassertr(_current_sort < _active.front()->get_sort(), true);
|
|
|
- _current_sort = _active.front()->get_sort();
|
|
|
- _cvar.signal_all();
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- // There are no more tasks in this epoch; advance to the next epoch.
|
|
|
- if (_tick_clock) {
|
|
|
- _clock->tick();
|
|
|
- }
|
|
|
- if (!_threads.empty()) {
|
|
|
- PStatClient::thread_tick(get_name());
|
|
|
- }
|
|
|
-
|
|
|
- _active.swap(_next_active);
|
|
|
-
|
|
|
- // Check for any sleeping tasks that need to be woken.
|
|
|
- double now = _clock->get_frame_time();
|
|
|
- while (!_sleeping.empty() && _sleeping.front()->get_wake_time() <= now) {
|
|
|
- PT(AsyncTask) task = _sleeping.front();
|
|
|
- pop_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
|
|
|
- _sleeping.pop_back();
|
|
|
- task->_state = AsyncTask::S_active;
|
|
|
- _active.push_back(task);
|
|
|
- }
|
|
|
-
|
|
|
- make_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
|
|
|
- nassertr(_num_tasks == _active.size() + _sleeping.size(), true);
|
|
|
-
|
|
|
- if (!_active.empty()) {
|
|
|
- // Get the first task on the queue.
|
|
|
- _current_sort = _active.front()->get_sort();
|
|
|
- _cvar.signal_all();
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- // There are no tasks to be had anywhere. Chill.
|
|
|
- _current_sort = INT_MAX;
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::do_stop_threads
|
|
|
-// Access: Protected
|
|
|
-// Description: The private implementation of stop_threads; assumes
|
|
|
-// the lock is already held.
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-do_stop_threads() {
|
|
|
- if (_state == S_started || _state == S_aborting) {
|
|
|
- _state = S_shutdown;
|
|
|
- _cvar.signal_all();
|
|
|
-
|
|
|
- Threads wait_threads;
|
|
|
- wait_threads.swap(_threads);
|
|
|
-
|
|
|
- // We have to release the lock while we join, so the threads can
|
|
|
- // wake up and see that we're shutting down.
|
|
|
- _lock.release();
|
|
|
- Threads::iterator ti;
|
|
|
- for (ti = wait_threads.begin(); ti != wait_threads.end(); ++ti) {
|
|
|
- (*ti)->join();
|
|
|
- }
|
|
|
- _lock.lock();
|
|
|
-
|
|
|
- _state = S_initial;
|
|
|
- nassertv(_num_busy_threads == 0);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::do_start_threads
|
|
|
-// Access: Protected
|
|
|
-// Description: The private implementation of start_threads; assumes
|
|
|
-// the lock is already held.
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-do_start_threads() {
|
|
|
- if (_state == S_aborting) {
|
|
|
- do_stop_threads();
|
|
|
- }
|
|
|
-
|
|
|
- if (_state == S_initial) {
|
|
|
- _state = S_started;
|
|
|
- _num_busy_threads = 0;
|
|
|
- if (Thread::is_threading_supported()) {
|
|
|
- _threads.reserve(_num_threads);
|
|
|
- for (int i = 0; i < _num_threads; ++i) {
|
|
|
- ostringstream strm;
|
|
|
- strm << get_name() << "_" << i;
|
|
|
- PT(AsyncTaskManagerThread) thread = new AsyncTaskManagerThread(strm.str(), this);
|
|
|
- if (thread->start(TP_low, true)) {
|
|
|
- _threads.push_back(thread);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::do_poll
|
|
|
-// Access: Protected
|
|
|
-// Description: The private implementation of poll(), this assumes
|
|
|
-// the lock is already held.
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::
|
|
|
-do_poll() {
|
|
|
- while (!_active.empty() && _state != S_shutdown && _state != S_aborting) {
|
|
|
- _current_sort = _active.front()->get_sort();
|
|
|
- service_one_task(NULL);
|
|
|
- }
|
|
|
-
|
|
|
- if (_state != S_shutdown && _state != S_aborting) {
|
|
|
- finish_sort_group();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::AsyncTaskManagerThread::Constructor
|
|
|
-// Access: Public
|
|
|
-// Description:
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-AsyncTaskManager::AsyncTaskManagerThread::
|
|
|
-AsyncTaskManagerThread(const string &name, AsyncTaskManager *manager) :
|
|
|
- Thread(name, manager->get_name()),
|
|
|
- _manager(manager),
|
|
|
- _servicing(NULL)
|
|
|
-{
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: AsyncTaskManager::AsyncTaskManagerThread::thread_main
|
|
|
-// Access: Public, Virtual
|
|
|
-// Description:
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-void AsyncTaskManager::AsyncTaskManagerThread::
|
|
|
-thread_main() {
|
|
|
- MutexHolder holder(_manager->_lock);
|
|
|
- while (_manager->_state != S_shutdown && _manager->_state != S_aborting) {
|
|
|
- if (!_manager->_active.empty() &&
|
|
|
- _manager->_active.front()->get_sort() == _manager->_current_sort) {
|
|
|
- PStatTimer timer(_task_pcollector);
|
|
|
- _manager->_num_busy_threads++;
|
|
|
- _manager->service_one_task(this);
|
|
|
- _manager->_num_busy_threads--;
|
|
|
- _manager->_cvar.signal_all();
|
|
|
-
|
|
|
- } else {
|
|
|
- // We've finished all the available tasks of the current sort
|
|
|
- // value. We can't pick up a new task until all of the threads
|
|
|
- // finish the tasks with the same sort value.
|
|
|
- if (_manager->_num_busy_threads == 0) {
|
|
|
- // We're the last thread to finish. Update _current_sort.
|
|
|
- if (!_manager->finish_sort_group()) {
|
|
|
- // Nothing to do. Wait for more tasks to be added.
|
|
|
- if (_manager->_sleeping.empty()) {
|
|
|
- PStatTimer timer(_wait_pcollector);
|
|
|
- _manager->_cvar.wait();
|
|
|
- } else {
|
|
|
- double wake_time = _manager->_sleeping.front()->get_wake_time();
|
|
|
- double now = _manager->_clock->get_frame_time();
|
|
|
- double timeout = max(wake_time - now, 0.0);
|
|
|
- PStatTimer timer(_wait_pcollector);
|
|
|
- _manager->_cvar.wait(timeout);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- } else {
|
|
|
- // Wait for the other threads to finish their current task
|
|
|
- // before we continue.
|
|
|
- PStatTimer timer(_wait_pcollector);
|
|
|
- _manager->_cvar.wait();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|