28 using std::ostringstream;
43 _cvar(manager->_lock),
45 _timeslice_priority(false),
47 _thread_priority(TP_normal),
52 _num_awaiting_tasks(0),
54 _current_sort(-INT_MAX),
56 _needs_cleanup(false),
59 _block_till_next_frame(false),
60 _next_implicit_sort(0)
85 _tick_clock = tick_clock;
103 nassertv(num_threads >= 0);
105 if (task_cat.is_debug()) {
106 do_output(task_cat.debug());
107 task_cat.debug(
false)
108 <<
": set_num_threads(" << num_threads <<
")\n";
111 if (!Thread::is_threading_supported()) {
116 if (_num_threads != num_threads) {
118 _num_threads = num_threads;
120 if (_num_tasks != 0) {
144 return _threads.size();
154 if (_thread_priority != priority) {
156 _thread_priority = priority;
158 if (_num_tasks != 0) {
170 return _thread_priority;
184 _frame_budget = frame_budget;
194 return _frame_budget;
214 _frame_sync = frame_sync;
246 _timeslice_priority = timeslice_priority;
257 return _timeslice_priority;
267 if (_state == S_started || _state == S_interrupted) {
281 if (_state == S_initial || _state == S_interrupted) {
295 if (task->_chain !=
this) {
296 nassertr(!do_has_task(task),
false);
300 if (task->_state == AsyncTask::S_servicing_removed) {
347 return do_get_active_tasks();
357 return do_get_sleeping_tasks();
382 return do_get_next_wake_time();
388 void AsyncTaskChain::
389 output(ostream &out)
const {
397 void AsyncTaskChain::
398 write(ostream &out,
int indent_level)
const {
400 do_write(out, indent_level);
410 void AsyncTaskChain::
412 nassertv(task->_chain ==
nullptr &&
413 task->_manager ==
nullptr &&
414 task->_chain_name == get_name() &&
415 task->_state == AsyncTask::S_inactive);
416 nassertv(!do_has_task(task));
421 task->_manager = _manager;
423 double now = _manager->_clock->get_frame_time();
424 task->_start_time = now;
425 task->_start_frame = _manager->_clock->get_frame_count();
428 task->_implicit_sort = _next_implicit_sort++;
430 _manager->add_task_by_name(task);
434 task->_wake_time = now + task->
get_delay();
435 task->_start_time = task->_wake_time;
436 task->_state = AsyncTask::S_sleeping;
437 _sleeping.push_back(task);
438 push_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
442 task->_state = AsyncTask::S_active;
443 if (task_cat.is_spam()) {
445 <<
"Adding " << *task <<
" with sort " << task->
get_sort()
446 <<
" to chain " << get_name() <<
" with current_sort " 447 << _current_sort <<
"\n";
449 if (task->
get_sort() >= _current_sort) {
451 _active.push_back(task);
452 push_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
455 _next_active.push_back(task);
459 ++(_manager->_num_tasks);
460 _needs_cleanup =
true;
470 bool AsyncTaskChain::
471 do_remove(
AsyncTask *task,
bool upon_death) {
472 nassertr(task->_chain ==
this,
false);
474 switch (task->_state) {
475 case AsyncTask::S_servicing:
477 task->_state = AsyncTask::S_servicing_removed;
480 case AsyncTask::S_servicing_removed:
484 case AsyncTask::S_sleeping:
487 int index = find_task_on_heap(_sleeping, task);
488 nassertr(index != -1,
false);
490 _sleeping.erase(_sleeping.begin() + index);
491 make_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
492 cleanup_task(task, upon_death,
false);
496 case AsyncTask::S_active:
500 int index = find_task_on_heap(_active, task);
502 _active.erase(_active.begin() + index);
503 make_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
505 index = find_task_on_heap(_next_active, task);
507 _next_active.erase(_next_active.begin() + index);
509 index = find_task_on_heap(_this_active, task);
510 nassertr(index != -1,
false);
513 cleanup_task(task, upon_death,
false);
527 void AsyncTaskChain::
528 do_wait_for_tasks() {
531 if (_threads.empty()) {
533 while (_num_tasks > 0) {
534 if (_state == S_shutdown || _state == S_interrupted) {
542 while (_num_tasks > 0) {
543 if (_state == S_shutdown || _state == S_interrupted) {
557 void AsyncTaskChain::
559 if (task_cat.is_spam()) {
560 do_output(task_cat.spam());
562 <<
": do_cleanup()\n";
574 dead.reserve(_num_tasks);
576 _needs_cleanup =
false;
578 TaskHeap::const_iterator ti;
579 for (ti = _active.begin(); ti != _active.end(); ++ti) {
581 dead.push_back(task);
582 cleanup_task(task,
false,
false);
584 for (ti = _this_active.begin(); ti != _this_active.end(); ++ti) {
586 dead.push_back(task);
587 cleanup_task(task,
false,
false);
589 for (ti = _next_active.begin(); ti != _next_active.end(); ++ti) {
591 dead.push_back(task);
592 cleanup_task(task,
false,
false);
594 for (ti = _sleeping.begin(); ti != _sleeping.end(); ++ti) {
596 dead.push_back(task);
597 cleanup_task(task,
false,
false);
601 nassertv(_num_tasks == 0 || _num_tasks == 1);
605 for (ti = dead.begin(); ti != dead.end(); ++ti) {
606 (*ti)->upon_death(_manager,
false);
608 _manager->_lock.
lock();
610 if (task_cat.is_spam()) {
611 do_output(task_cat.spam());
613 <<
": done do_cleanup()\n";
622 bool AsyncTaskChain::
624 return (find_task_on_heap(_active, task) != -1 ||
625 find_task_on_heap(_next_active, task) != -1 ||
626 find_task_on_heap(_sleeping, task) != -1 ||
627 find_task_on_heap(_this_active, task) != -1);
636 find_task_on_heap(
const TaskHeap &heap,
AsyncTask *task)
const {
637 for (
int i = 0; i < (int)heap.size(); ++i) {
638 if (heap[i] == task) {
653 void AsyncTaskChain::
654 service_one_task(AsyncTaskChain::AsyncTaskChainThread *thread) {
655 if (!_active.empty()) {
657 pop_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
660 if (thread !=
nullptr) {
661 thread->_servicing = task;
664 if (task_cat.is_spam()) {
666 <<
"Servicing " << *task <<
" in " 670 nassertv(task->
get_sort() == _current_sort);
671 nassertv(task->_state == AsyncTask::S_active);
672 task->_state = AsyncTask::S_servicing;
673 task->_servicing_thread = thread;
675 AsyncTask::DoneStatus ds = task->unlock_and_do_task();
677 if (thread !=
nullptr) {
678 thread->_servicing =
nullptr;
680 task->_servicing_thread =
nullptr;
682 if (task->_chain ==
this) {
683 if (task->_state == AsyncTask::S_servicing_removed) {
685 cleanup_task(task,
true,
false);
687 }
else if (task->_chain_name != get_name()) {
690 cleanup_task(task,
false,
false);
691 task->jump_to_task_chain(_manager);
695 case AsyncTask::DS_cont:
697 task->_state = AsyncTask::S_active;
698 _next_active.push_back(task);
702 case AsyncTask::DS_again:
705 double now = _manager->_clock->get_frame_time();
706 task->_wake_time = now + task->
get_delay();
707 task->_start_time = task->_wake_time;
708 task->_state = AsyncTask::S_sleeping;
709 _sleeping.push_back(task);
710 push_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
711 if (task_cat.is_spam()) {
713 <<
"Sleeping " << *task <<
", wake time at " 714 << task->_wake_time - now <<
"\n";
720 case AsyncTask::DS_pickup:
722 task->_state = AsyncTask::S_active;
723 _this_active.push_back(task);
727 case AsyncTask::DS_interrupt:
729 task->_state = AsyncTask::S_active;
730 _next_active.push_back(task);
731 if (_state == S_started) {
732 _state = S_interrupted;
737 case AsyncTask::DS_await:
739 task->_state = AsyncTask::S_awaiting;
741 ++_num_awaiting_tasks;
746 cleanup_task(task,
true,
true);
751 <<
"Task is no longer on chain " << get_name()
752 <<
": " << *task <<
"\n";
755 if (task_cat.is_spam()) {
757 <<
"Done servicing " << *task <<
" in " 761 thread_consider_yield();
773 void AsyncTaskChain::
774 cleanup_task(
AsyncTask *task,
bool upon_death,
bool clean_exit) {
775 if (task_cat.is_spam()) {
776 do_output(task_cat.spam());
778 <<
": cleanup_task(" << *task <<
", " << upon_death <<
", " << clean_exit
782 nassertv(task->_chain ==
this);
784 task->_state = AsyncTask::S_inactive;
785 task->_chain =
nullptr;
787 --(_manager->_num_tasks);
789 _manager->remove_task_by_name(task);
791 if (upon_death && task->set_future_state(clean_exit ? AsyncFuture::FS_finished
792 : AsyncFuture::FS_cancelled)) {
796 task->_manager =
nullptr;
800 task->upon_death(_manager, clean_exit);
801 _manager->_lock.
lock();
813 bool AsyncTaskChain::
814 finish_sort_group() {
815 nassertr(_num_busy_threads == 0,
true);
817 if (!_threads.empty()) {
818 PStatClient::thread_tick(get_name());
821 if (!_active.empty()) {
823 nassertr(_current_sort < _active.front()->get_sort(),
true);
824 _current_sort = _active.front()->get_sort();
831 if (!_this_active.empty() && _frame_budget >= 0.0) {
836 if (task_cat.is_spam()) {
837 do_output(task_cat.spam());
839 <<
": next epoch (pickup mode)\n";
843 _active.swap(_this_active);
848 if (task_cat.is_spam()) {
849 do_output(task_cat.spam());
854 _pickup_mode =
false;
858 _next_active.insert(_next_active.end(), _this_active.begin(), _this_active.end());
859 _this_active.clear();
861 _active.swap(_next_active);
868 if (task_cat.is_spam()) {
869 do_output(task_cat.spam());
873 _manager->_clock->tick();
876 }
else if (_frame_sync) {
879 _block_till_next_frame =
true;
883 double now = _manager->_clock->get_frame_time();
884 while (!_sleeping.empty() && _sleeping.front()->_wake_time <= now) {
886 if (task_cat.is_spam()) {
888 <<
"Waking " << *task <<
", wake time at " 889 << task->_wake_time - now <<
"\n";
891 pop_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
892 _sleeping.pop_back();
893 task->_state = AsyncTask::S_active;
894 task->_start_frame = _manager->_clock->get_frame_count();
895 _active.push_back(task);
898 if (task_cat.is_spam()) {
899 if (_sleeping.empty()) {
901 <<
"No more tasks on sleeping queue.\n";
904 <<
"Next sleeper: " << *_sleeping.front() <<
", wake time at " 905 << _sleeping.front()->_wake_time - now <<
"\n";
911 TaskHeap::const_iterator ti;
912 for (ti = _active.begin(); ti != _active.end(); ++ti) {
918 if (_timeslice_priority) {
919 filter_timeslice_priority();
922 nassertr((
size_t)_num_tasks == _active.size() + _this_active.size() + _next_active.size() + _sleeping.size() + (size_t)_num_awaiting_tasks,
true);
923 make_heap(_active.begin(), _active.end(), AsyncTaskSortPriority());
925 _current_sort = -INT_MAX;
927 if (!_active.empty()) {
934 _pickup_mode =
false;
935 nassertr(_this_active.empty(),
false);
946 void AsyncTaskChain::
947 filter_timeslice_priority() {
948 if (_active.empty()) {
951 nassertv(_timeslice_priority);
954 double net_runtime = 0.0;
955 int net_priority = 0;
957 TaskHeap::iterator ti;
958 for (ti = _active.begin(); ti != _active.end(); ++ti) {
961 int priority = max(task->_priority, 1);
962 net_runtime += runtime;
963 net_priority += priority;
967 double average_budget = net_runtime / (double)net_priority;
969 TaskHeap keep, postpone;
970 for (ti = _active.begin(); ti != _active.end(); ++ti) {
973 int priority = max(task->_priority, 1);
974 double consumed = runtime / (double)priority;
977 if (consumed > average_budget) {
979 postpone.push_back(task);
982 keep.push_back(task);
989 nassertv(!postpone.empty());
990 ti = postpone.begin();
991 TaskHeap::iterator max_ti = ti;
993 while (ti != postpone.end()) {
994 if ((*ti)->_priority > (*max_ti)->_priority) {
1001 keep.push_back(*max_ti);
1002 postpone.erase(max_ti);
1007 _this_active.insert(_this_active.end(), postpone.begin(), postpone.end());
1009 _next_active.insert(_next_active.end(), postpone.begin(), postpone.end());
1012 nassertv(!_active.empty());
1019 void AsyncTaskChain::
1021 if (_state == S_started || _state == S_interrupted) {
1022 if (task_cat.is_debug() && !_threads.empty()) {
1024 <<
"Stopping " << _threads.size()
1025 <<
" threads for " << _manager->get_name()
1026 <<
" chain " << get_name()
1030 _state = S_shutdown;
1034 Threads wait_threads;
1035 wait_threads.swap(_threads);
1039 _manager->_lock.
unlock();
1040 Threads::iterator ti;
1041 for (ti = wait_threads.begin(); ti != wait_threads.end(); ++ti) {
1042 if (task_cat.is_debug()) {
1044 <<
"Waiting for " << *(*ti) <<
" in " 1048 if (task_cat.is_spam()) {
1050 <<
"Done waiting for " << *(*ti) <<
" in " 1054 _manager->_lock.
lock();
1059 nassertv(_num_busy_threads == 0 || _num_busy_threads == 1);
1060 cleanup_pickup_mode();
1068 void AsyncTaskChain::
1069 do_start_threads() {
1070 if (_state == S_interrupted) {
1074 if (_state == S_initial) {
1076 if (Thread::is_threading_supported() && _num_threads > 0) {
1077 if (task_cat.is_debug()) {
1079 <<
"Starting " << _num_threads <<
" threads for " 1080 << _manager->get_name() <<
" chain " << get_name() <<
"\n";
1082 _needs_cleanup =
true;
1083 _threads.reserve(_num_threads);
1084 for (
int i = 0; i < _num_threads; ++i) {
1086 strm << _manager->get_name() <<
"_" << get_name() <<
"_" << i;
1087 PT(AsyncTaskChainThread) thread =
new AsyncTaskChainThread(strm.str(),
this);
1088 if (thread->start(_thread_priority,
true)) {
1089 _threads.push_back(thread);
1101 do_get_active_tasks()
const {
1104 Threads::const_iterator thi;
1105 for (thi = _threads.begin(); thi != _threads.end(); ++thi) {
1107 if (task !=
nullptr) {
1111 TaskHeap::const_iterator ti;
1112 for (ti = _active.begin(); ti != _active.end(); ++ti) {
1116 for (ti = _this_active.begin(); ti != _this_active.end(); ++ti) {
1120 for (ti = _next_active.begin(); ti != _next_active.end(); ++ti) {
1133 do_get_sleeping_tasks()
const {
1136 TaskHeap::const_iterator ti;
1137 for (ti = _sleeping.begin(); ti != _sleeping.end(); ++ti) {
1149 void AsyncTaskChain::
1151 thread_consider_yield();
1152 if (_num_tasks == 0) {
1158 if (!_threads.empty()) {
1162 if (_num_busy_threads != 0) {
1165 <<
"Ignoring recursive poll() within another task.\n";
1169 nassertv(!_pickup_mode);
1172 while (!_active.empty()) {
1173 if (_state == S_shutdown || _state == S_interrupted) {
1176 int frame = _manager->_clock->get_frame_count();
1177 if (_current_frame != frame) {
1178 _current_frame = frame;
1179 _time_in_frame = 0.0;
1180 _block_till_next_frame =
false;
1182 if (_block_till_next_frame ||
1183 (_frame_budget >= 0.0 && _time_in_frame >= _frame_budget)) {
1186 cleanup_pickup_mode();
1190 _current_sort = _active.front()->get_sort();
1195 _num_busy_threads++;
1196 service_one_task(
nullptr);
1197 _num_busy_threads--;
1200 if (!_threads.empty()) {
1205 finish_sort_group();
1206 }
while (_pickup_mode);
1214 void AsyncTaskChain::
1215 cleanup_pickup_mode() {
1217 _pickup_mode =
false;
1220 _next_active.insert(_next_active.end(), _this_active.begin(), _this_active.end());
1221 _this_active.clear();
1222 _next_active.insert(_next_active.end(), _active.begin(), _active.end());
1226 finish_sort_group();
1234 void AsyncTaskChain::
1235 do_output(ostream &out)
const {
1236 if (_manager !=
nullptr) {
1237 out << _manager->get_type() <<
" " << _manager->get_name();
1239 out <<
"(no manager)";
1241 out <<
" task chain " << get_name()
1242 <<
"; " << _num_tasks <<
" tasks";
1249 void AsyncTaskChain::
1250 do_write(ostream &out,
int indent_level)
const {
1251 indent(out, indent_level)
1252 <<
"Task chain \"" << get_name() <<
"\"\n";
1253 if (_num_threads > 0) {
1254 indent(out, indent_level + 2)
1255 << _num_threads <<
" threads, priority " << _thread_priority <<
"\n";
1257 if (_frame_budget >= 0.0) {
1258 indent(out, indent_level + 2)
1259 <<
"frame budget " << _frame_budget <<
" s\n";
1261 if (_timeslice_priority) {
1262 indent(out, indent_level + 2)
1263 <<
"timeslice priority\n";
1266 indent(out, indent_level + 2)
1270 static const size_t buffer_size = 1024;
1271 char buffer[buffer_size];
1272 sprintf(buffer,
" %-32s %8s %8s %8s %8s %6s",
1275 "dt(ms)",
"avg",
"max",
1277 nassertv(strlen(buffer) < buffer_size);
1279 indent(out, indent_level)
1282 indent(out, indent_level);
1283 for (
int i = 0; i < 32+8+8+8+8+6+7; ++i) {
1289 TaskHeap tasks = _active;
1290 tasks.insert(tasks.end(), _this_active.begin(), _this_active.end());
1291 tasks.insert(tasks.end(), _next_active.begin(), _next_active.end());
1293 Threads::const_iterator thi;
1294 for (thi = _threads.begin(); thi != _threads.end(); ++thi) {
1296 if (task !=
nullptr) {
1297 tasks.push_back(task);
1301 double now = _manager->_clock->get_frame_time();
1303 if (!tasks.empty()) {
1304 sort(tasks.begin(), tasks.end(), AsyncTaskSortPriority());
1309 TaskHeap::reverse_iterator ti;
1310 for (ti = tasks.rbegin(); ti != tasks.rend(); ++ti) {
1312 write_task_line(out, indent_level, task, now);
1319 TaskHeap sleeping = _sleeping;
1320 while (!sleeping.empty()) {
1322 pop_heap(sleeping.begin(), sleeping.end(), AsyncTaskSortWakeTime());
1323 sleeping.pop_back();
1325 write_task_line(out, indent_level, task, now);
1333 void AsyncTaskChain::
1334 write_task_line(ostream &out,
int indent_level,
AsyncTask *task,
double now)
const {
1335 char servicing_flag =
' ';
1336 if (task->_state == AsyncTask::S_servicing) {
1337 servicing_flag =
'*';
1338 }
else if (task->_state == AsyncTask::S_servicing_removed) {
1339 servicing_flag =
'-';
1342 static const size_t buffer_size = 1024;
1343 char buffer[buffer_size];
1345 if (task->_state == AsyncTask::S_sleeping) {
1348 string name = task->get_name().substr(0, 32);
1349 sprintf(buffer,
"%c%-32s %8.1f",
1350 servicing_flag, name.c_str(),
1351 task->_wake_time - now);
1355 string name = task->get_name().substr(0, 41);
1356 sprintf(buffer,
"%c%-41s",
1357 servicing_flag, name.c_str());
1359 nassertv(strlen(buffer) < buffer_size);
1361 indent(out, indent_level)
1364 if (task->_num_frames > 0) {
1365 sprintf(buffer,
" %8.1f %8.1f %8.1f %6d",
1367 task->_max_dt * 1000.0,
1371 sprintf(buffer,
" %8s %8s %8s %6d",
1376 nassertv(strlen(buffer) < buffer_size);
1377 out << buffer <<
"\n";
1383 AsyncTaskChain::AsyncTaskChainThread::
1384 AsyncTaskChainThread(
const string &name,
AsyncTaskChain *chain) :
1385 Thread(name, chain->get_name()),
1394 void AsyncTaskChain::AsyncTaskChainThread::
1397 while (_chain->_state != S_shutdown && _chain->_state != S_interrupted) {
1398 thread_consider_yield();
1399 if (!_chain->_active.empty() &&
1400 _chain->_active.front()->get_sort() == _chain->_current_sort) {
1402 int frame = _chain->_manager->_clock->get_frame_count();
1403 if (_chain->_current_frame != frame) {
1404 _chain->_current_frame = frame;
1405 _chain->_time_in_frame = 0.0;
1406 _chain->_block_till_next_frame =
false;
1410 if (_chain->_block_till_next_frame ||
1411 (_chain->_frame_budget >= 0.0 && _chain->_time_in_frame >= _chain->_frame_budget)) {
1412 while ((_chain->_block_till_next_frame ||
1413 (_chain->_frame_budget >= 0.0 && _chain->_time_in_frame >= _chain->_frame_budget)) &&
1414 _chain->_state != S_shutdown && _chain->_state != S_interrupted) {
1415 _chain->cleanup_pickup_mode();
1416 _chain->_manager->_frame_cvar.wait();
1417 frame = _chain->_manager->_clock->get_frame_count();
1418 if (_chain->_current_frame != frame) {
1419 _chain->_current_frame = frame;
1420 _chain->_time_in_frame = 0.0;
1421 _chain->_block_till_next_frame =
false;
1429 _chain->_num_busy_threads++;
1430 _chain->service_one_task(
this);
1431 _chain->_num_busy_threads--;
1432 _chain->_cvar.notify_all();
1438 if (_chain->_num_busy_threads == 0) {
1440 if (!_chain->finish_sort_group()) {
1442 if (_chain->_sleeping.empty()) {
1444 _chain->_cvar.wait();
1446 double wake_time = _chain->do_get_next_wake_time();
1447 double now = _chain->_manager->_clock->get_frame_time();
1448 double timeout = max(wake_time - now, 0.0);
1450 _chain->_cvar.wait(timeout);
1458 _chain->_cvar.wait();
bool get_timeslice_priority() const
Returns the timeslice_priority flag.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
AsyncTaskCollection get_tasks() const
Returns the set of tasks that are active or sleeping on the task chain, at the time of the call...
void wait()
Waits on the condition.
void notify_done(bool clean_exit)
Schedules the done callbacks.
A class to manage a loose queue of isolated tasks, which can be performed either synchronously (in th...
void set_frame_sync(bool frame_sync)
Sets the frame_sync flag.
bool get_tick_clock() const
Returns the tick_clock flag.
double get_frame_budget() const
Returns the maximum amount of time per frame the tasks on this chain are granted for execution...
void add_task(AsyncTask *task)
Adds a new AsyncTask to the collection.
A list of tasks, for instance as returned by some of the AsyncTaskManager query functions.
bool has_delay() const
Returns true if a delay has been set for this task via set_delay(), or false otherwise.
void start_threads()
Starts any requested threads to service the tasks on the queue.
get_sort
Returns the task's current sort value.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
void unlock()
Alias for release() to match C++11 semantics.
void wait_for_tasks()
Blocks until the task list is empty.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_num_threads(int num_threads)
Changes the number of threads for this task chain.
void notify_all()
Informs all of the other threads who are currently blocked on wait() that the relevant condition has ...
A lightweight class that represents a single element that may be timed and/or counted via stats...
void set_frame_budget(double frame_budget)
Sets the maximum amount of time per frame the tasks on this chain are granted for execution...
get_current_thread
Returns a pointer to the currently-executing Thread object.
A base class for all things which can have a name.
bool get_frame_sync() const
Returns the frame_sync flag.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
int get_num_running_threads() const
Returns the number of threads that have been created and are actively running.
void add_tasks_from(const AsyncTaskCollection &other)
Adds all the AsyncTasks indicated in the other collection to this task.
ThreadPriority get_thread_priority() const
Returns the priority associated with threads that serve this task chain.
int get_num_tasks() const
Returns the number of tasks that are currently active or sleeping within the task chain...
The AsyncTaskChain is a subset of the AsyncTaskManager.
AsyncTaskCollection get_active_tasks() const
Returns the set of tasks that are active (and not sleeping) on the task chain, at the time of the cal...
int get_num_threads() const
Returns the number of threads that will be servicing tasks for this chain.
get_average_dt
Returns the average amount of time elapsed during each of the task's previous run cycles...
void poll()
Runs through all the tasks in the task list, once, if the task chain is running in single-threaded mo...
This class represents a concrete task performed by an AsyncManager.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A thread; that is, a lightweight process.
void set_tick_clock(bool tick_clock)
Sets the tick_clock flag.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_timeslice_priority(bool timeslice_priority)
Sets the timeslice_priority flag.
void stop_threads()
Stops any threads that are currently running.
TypeHandle is the identifier used to differentiate C++ class types.
void set_thread_priority(ThreadPriority priority)
Changes the priority associated with threads that serve this task chain.
double get_next_wake_time() const
Returns the scheduled time (on the manager's clock) of the next sleeping task, on any task chain...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void lock()
Alias for acquire() to match C++11 semantics.
bool has_task(AsyncTask *task) const
Returns true if the indicated task has been added to this AsyncTaskChain, false otherwise.
double get_delay() const
Returns the delay value that has been set via set_delay, if any.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
AsyncTaskCollection get_sleeping_tasks() const
Returns the set of tasks that are sleeping (and not active) on the task chain, at the time of the cal...