00001 // Filename: asyncTask.cxx 00002 // Created by: drose (23Aug06) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "asyncTask.h" 00016 #include "asyncTaskManager.h" 00017 #include "config_event.h" 00018 #include "pt_Event.h" 00019 #include "throw_event.h" 00020 #include "eventParameter.h" 00021 00022 AtomicAdjust::Integer AsyncTask::_next_task_id; 00023 PStatCollector AsyncTask::_show_code_pcollector("App:Show code"); 00024 TypeHandle AsyncTask::_type_handle; 00025 00026 //////////////////////////////////////////////////////////////////// 00027 // Function: AsyncTask::Constructor 00028 // Access: Public 00029 // Description: 00030 //////////////////////////////////////////////////////////////////// 00031 AsyncTask:: 00032 AsyncTask(const string &name) : 00033 _chain_name("default"), 00034 _delay(0.0), 00035 _has_delay(false), 00036 _wake_time(0.0), 00037 _sort(0), 00038 _priority(0), 00039 _state(S_inactive), 00040 _servicing_thread(NULL), 00041 _manager(NULL), 00042 _chain(NULL), 00043 _start_time(0.0), 00044 _start_frame(0), 00045 _dt(0.0), 00046 _max_dt(0.0), 00047 _total_dt(0.0), 00048 _num_frames(0) 00049 { 00050 #ifdef HAVE_PYTHON 00051 _python_object = NULL; 00052 #endif // HAVE_PYTHON 00053 set_name(name); 00054 00055 // Carefully copy _next_task_id and increment it so that we get a 00056 // unique ID. 00057 AtomicAdjust::Integer current_id = _next_task_id; 00058 while (AtomicAdjust::compare_and_exchange(_next_task_id, current_id, current_id + 1) != current_id) { 00059 current_id = _next_task_id; 00060 } 00061 00062 _task_id = current_id; 00063 } 00064 00065 //////////////////////////////////////////////////////////////////// 00066 // Function: AsyncTask::Destructor 00067 // Access: Public, Virtual 00068 // Description: 00069 //////////////////////////////////////////////////////////////////// 00070 AsyncTask:: 00071 ~AsyncTask() { 00072 nassertv(_state == S_inactive && _manager == NULL && _chain == NULL); 00073 #ifdef HAVE_PYTHON 00074 set_python_object(NULL); 00075 #endif 00076 } 00077 00078 //////////////////////////////////////////////////////////////////// 00079 // Function: AsyncTask::remove 00080 // Access: Published 00081 // Description: Removes the task from its active manager, if any, and 00082 // makes the state S_inactive (or possible 00083 // S_servicing_removed). This is a no-op if the state 00084 // is already S_inactive. 00085 //////////////////////////////////////////////////////////////////// 00086 void AsyncTask:: 00087 remove() { 00088 if (_manager != (AsyncTaskManager *)NULL) { 00089 _manager->remove(this); 00090 } 00091 } 00092 00093 //////////////////////////////////////////////////////////////////// 00094 // Function: AsyncTask::get_wake_time 00095 // Access: Published 00096 // Description: If this task has been added to an AsyncTaskManager 00097 // with a delay in effect, this returns the time at 00098 // which the task is expected to awaken. It has no 00099 // meaning if the task has not yet been added to a 00100 // queue, or if there was no delay in effect at the time 00101 // the task was added. 00102 // 00103 // If the task's status is not S_sleeping, this returns 00104 // 0.0. 00105 //////////////////////////////////////////////////////////////////// 00106 double AsyncTask:: 00107 get_wake_time() const { 00108 if (_manager != (AsyncTaskManager *)NULL) { 00109 MutexHolder holder(_manager->_lock); 00110 if (_state == S_sleeping) { 00111 return _wake_time; 00112 } 00113 } 00114 00115 // If it's not on any manager, or it's not sleeping, the wake time 00116 // is 0.0. 00117 return 0.0; 00118 } 00119 00120 //////////////////////////////////////////////////////////////////// 00121 // Function: AsyncTask::recalc_wake_time 00122 // Access: Published 00123 // Description: If the task is currently sleeping on a task 00124 // chain, this resets its wake time to the current time 00125 // + get_delay(). It is as if the task had suddenly 00126 // returned DS_again. The task will sleep for its 00127 // current delay seconds before running again. This 00128 // method may therefore be used to make the task wake up 00129 // sooner or later than it would have otherwise. 00130 // 00131 // If the task is not already sleeping, this method has 00132 // no effect. 00133 //////////////////////////////////////////////////////////////////// 00134 void AsyncTask:: 00135 recalc_wake_time() { 00136 if (_manager != (AsyncTaskManager *)NULL) { 00137 MutexHolder holder(_manager->_lock); 00138 if (_state == S_sleeping) { 00139 double now = _manager->_clock->get_frame_time(); 00140 _wake_time = now + _delay; 00141 _start_time = _wake_time; 00142 00143 make_heap(_chain->_sleeping.begin(), _chain->_sleeping.end(), 00144 AsyncTaskChain::AsyncTaskSortWakeTime()); 00145 } 00146 } 00147 } 00148 00149 //////////////////////////////////////////////////////////////////// 00150 // Function: AsyncTask::get_elapsed_time 00151 // Access: Published 00152 // Description: Returns the amount of time that has elapsed since 00153 // the task was started, according to the task manager's 00154 // clock. 00155 // 00156 // It is only valid to call this if the task's status is 00157 // not S_inactive. 00158 //////////////////////////////////////////////////////////////////// 00159 double AsyncTask:: 00160 get_elapsed_time() const { 00161 nassertr(_state != S_inactive, 0.0); 00162 nassertr(_manager != (AsyncTaskManager *)NULL, 0.0); 00163 return _manager->_clock->get_frame_time() - _start_time; 00164 } 00165 00166 //////////////////////////////////////////////////////////////////// 00167 // Function: AsyncTask::get_elapsed_frames 00168 // Access: Published 00169 // Description: Returns the number of frames that have elapsed since 00170 // the task was started, according to the task manager's 00171 // clock. 00172 // 00173 // It is only valid to call this if the task's status is 00174 // not S_inactive. 00175 //////////////////////////////////////////////////////////////////// 00176 int AsyncTask:: 00177 get_elapsed_frames() const { 00178 nassertr(_state != S_inactive, 0); 00179 nassertr(_manager != (AsyncTaskManager *)NULL, 0); 00180 return _manager->_clock->get_frame_count() - _start_frame; 00181 } 00182 00183 //////////////////////////////////////////////////////////////////// 00184 // Function: AsyncTask::set_name 00185 // Access: Published 00186 // Description: 00187 //////////////////////////////////////////////////////////////////// 00188 void AsyncTask:: 00189 set_name(const string &name) { 00190 if (_manager != (AsyncTaskManager *)NULL) { 00191 MutexHolder holder(_manager->_lock); 00192 if (Namable::get_name() != name) { 00193 // Changing an active task's name requires moving it around on 00194 // its name index. 00195 00196 _manager->remove_task_by_name(this); 00197 Namable::set_name(name); 00198 _manager->add_task_by_name(this); 00199 } 00200 } else { 00201 // If it hasn't been started anywhere, we can just change the 00202 // name. 00203 Namable::set_name(name); 00204 } 00205 00206 #ifdef DO_PSTATS 00207 // Update the PStatCollector with the new name. If the name 00208 // includes a colon, we stop the collector name there, and don't go 00209 // further. 00210 size_t end = name.size(); 00211 size_t colon = name.find(':'); 00212 if (colon != string::npos) { 00213 end = min(end, colon); 00214 } 00215 00216 // If the name ends with a hyphen followed by a string of digits, we 00217 // strip all that off, for the parent collector, to group related 00218 // tasks together in the pstats graph. We still create a child 00219 // collector that contains the full name, however. 00220 size_t trimmed = end; 00221 size_t p = trimmed; 00222 while (true) { 00223 while (p > 0 && isdigit(name[p - 1])) { 00224 --p; 00225 } 00226 if (p > 0 && (name[p - 1] == '-' || name[p - 1] == '_')) { 00227 --p; 00228 trimmed = p; 00229 } else { 00230 p = trimmed; 00231 break; 00232 } 00233 } 00234 PStatCollector parent(_show_code_pcollector, name.substr(0, trimmed)); 00235 // prevent memory leak 00236 //_task_pcollector = PStatCollector(parent, name.substr(0, end)); 00237 _task_pcollector = parent; 00238 #endif // DO_PSTATS 00239 } 00240 00241 //////////////////////////////////////////////////////////////////// 00242 // Function: AsyncTask::get_name_prefix 00243 // Access: Published 00244 // Description: Returns the initial part of the name, up to but not 00245 // including any trailing digits following a hyphen or 00246 // underscore. 00247 //////////////////////////////////////////////////////////////////// 00248 string AsyncTask:: 00249 get_name_prefix() const { 00250 string name = get_name(); 00251 size_t trimmed = name.size(); 00252 size_t p = trimmed; 00253 while (true) { 00254 while (p > 0 && isdigit(name[p - 1])) { 00255 --p; 00256 } 00257 if (p > 0 && (name[p - 1] == '-' || name[p - 1] == '_')) { 00258 --p; 00259 trimmed = p; 00260 } else { 00261 p = trimmed; 00262 break; 00263 } 00264 } 00265 00266 return name.substr(0, trimmed); 00267 } 00268 00269 //////////////////////////////////////////////////////////////////// 00270 // Function: AsyncTask::set_task_chain 00271 // Access: Published 00272 // Description: Specifies the AsyncTaskChain on which this task will 00273 // be running. Each task chain runs tasks independently 00274 // of the others. 00275 //////////////////////////////////////////////////////////////////// 00276 void AsyncTask:: 00277 set_task_chain(const string &chain_name) { 00278 if (chain_name != _chain_name) { 00279 if (_manager != (AsyncTaskManager *)NULL) { 00280 MutexHolder holder(_manager->_lock); 00281 if (_state == S_active) { 00282 // Changing chains on an "active" (i.e. enqueued) task means 00283 // removing it and re-inserting it into the queue. 00284 PT(AsyncTask) hold_task = this; 00285 PT(AsyncTaskManager) manager = _manager; 00286 00287 AsyncTaskChain *chain_a = manager->do_find_task_chain(_chain_name); 00288 nassertv(chain_a != (AsyncTaskChain *)NULL); 00289 chain_a->do_remove(this); 00290 _chain_name = chain_name; 00291 00292 jump_to_task_chain(manager); 00293 00294 } else { 00295 // If it's sleeping, currently being serviced, or something 00296 // else, we can just change the chain_name value directly. 00297 _chain_name = chain_name; 00298 } 00299 } else { 00300 // If it hasn't been started anywhere, we can just change the 00301 // chain_name value. 00302 _chain_name = chain_name; 00303 } 00304 } 00305 } 00306 00307 //////////////////////////////////////////////////////////////////// 00308 // Function: AsyncTask::set_sort 00309 // Access: Published 00310 // Description: Specifies a sort value for this task. Within a given 00311 // AsyncTaskManager, all of the tasks with a given sort 00312 // value are guaranteed to be completed before any tasks 00313 // with a higher sort value are begun. 00314 // 00315 // To put it another way, two tasks might execute in 00316 // parallel with each other only if they both have the 00317 // same sort value. Tasks with a lower sort value are 00318 // executed first. 00319 // 00320 // This is different from the priority, which makes no 00321 // such exclusion guarantees. 00322 //////////////////////////////////////////////////////////////////// 00323 void AsyncTask:: 00324 set_sort(int sort) { 00325 if (sort != _sort) { 00326 if (_manager != (AsyncTaskManager *)NULL) { 00327 MutexHolder holder(_manager->_lock); 00328 if (_state == S_active && _sort >= _chain->_current_sort) { 00329 // Changing sort on an "active" (i.e. enqueued) task means 00330 // removing it and re-inserting it into the queue. 00331 PT(AsyncTask) hold_task = this; 00332 AsyncTaskChain *chain = _manager->do_find_task_chain(_chain_name); 00333 nassertv(chain != (AsyncTaskChain *)NULL); 00334 chain->do_remove(this); 00335 _sort = sort; 00336 chain->do_add(this); 00337 00338 } else { 00339 // If it's sleeping, currently being serviced, or something 00340 // else, we can just change the sort value directly. 00341 _sort = sort; 00342 } 00343 } else { 00344 // If it hasn't been started anywhere, we can just change the 00345 // sort value. 00346 _sort = sort; 00347 } 00348 } 00349 } 00350 00351 //////////////////////////////////////////////////////////////////// 00352 // Function: AsyncTask::set_priority 00353 // Access: Published 00354 // Description: Specifies a priority value for this task. In 00355 // general, tasks with a higher priority value are 00356 // executed before tasks with a lower priority value 00357 // (but only for tasks with the same sort value). 00358 // 00359 // Unlike the sort value, tasks with different 00360 // priorities may execute at the same time, if the 00361 // AsyncTaskManager has more than one thread servicing 00362 // tasks. 00363 // 00364 // Also see AsyncTaskChain::set_timeslice_priority(), 00365 // which changes the meaning of this value. In the 00366 // default mode, when the timeslice_priority flag is 00367 // false, all tasks always run once per epoch, 00368 // regardless of their priority values (that is, the 00369 // priority controls the order of the task execution 00370 // only, not the number of times it runs). On the other 00371 // hand, if you set the timeslice_priority flag to true, 00372 // then changing a task's priority has an effect on the 00373 // number of times it runs. 00374 //////////////////////////////////////////////////////////////////// 00375 void AsyncTask:: 00376 set_priority(int priority) { 00377 if (priority != _priority) { 00378 if (_manager != (AsyncTaskManager *)NULL) { 00379 MutexHolder holder(_manager->_lock); 00380 if (_state == S_active && _sort >= _chain->_current_sort) { 00381 // Changing priority on an "active" (i.e. enqueued) task means 00382 // removing it and re-inserting it into the queue. 00383 PT(AsyncTask) hold_task = this; 00384 AsyncTaskChain *chain = _manager->do_find_task_chain(_chain_name); 00385 nassertv(chain != (AsyncTaskChain *)NULL); 00386 chain->do_remove(this); 00387 _priority = priority; 00388 chain->do_add(this); 00389 00390 } else { 00391 // If it's sleeping, currently being serviced, or something 00392 // else, we can just change the priority value directly. 00393 _priority = priority; 00394 } 00395 } else { 00396 // If it hasn't been started anywhere, we can just change the 00397 // priority value. 00398 _priority = priority; 00399 } 00400 } 00401 } 00402 00403 //////////////////////////////////////////////////////////////////// 00404 // Function: AsyncTask::output 00405 // Access: Published, Virtual 00406 // Description: 00407 //////////////////////////////////////////////////////////////////// 00408 void AsyncTask:: 00409 output(ostream &out) const { 00410 out << get_type(); 00411 if (has_name()) { 00412 out << " " << get_name(); 00413 } 00414 } 00415 00416 //////////////////////////////////////////////////////////////////// 00417 // Function: AsyncTask::jump_to_task_chain 00418 // Access: Protected 00419 // Description: Switches the AsyncTask to its new task chain, named 00420 // by _chain_name. Called internally only. 00421 //////////////////////////////////////////////////////////////////// 00422 void AsyncTask:: 00423 jump_to_task_chain(AsyncTaskManager *manager) { 00424 AsyncTaskChain *chain_b = manager->do_find_task_chain(_chain_name); 00425 if (chain_b == (AsyncTaskChain *)NULL) { 00426 task_cat.warning() 00427 << "Creating implicit AsyncTaskChain " << _chain_name 00428 << " for " << manager->get_type() << " " 00429 << manager->get_name() << "\n"; 00430 chain_b = manager->do_make_task_chain(_chain_name); 00431 } 00432 chain_b->do_add(this); 00433 } 00434 00435 //////////////////////////////////////////////////////////////////// 00436 // Function: AsyncTask::unlock_and_do_task 00437 // Access: Protected 00438 // Description: Called by the AsyncTaskManager to actually run the 00439 // task. Assumes the lock is held. See do_task(). 00440 //////////////////////////////////////////////////////////////////// 00441 AsyncTask::DoneStatus AsyncTask:: 00442 unlock_and_do_task() { 00443 nassertr(_manager != (AsyncTaskManager *)NULL, DS_done); 00444 PT(ClockObject) clock = _manager->get_clock(); 00445 00446 Thread *current_thread = Thread::get_current_thread(); 00447 record_task(current_thread); 00448 00449 // It's important to release the lock while the task is being 00450 // serviced. 00451 _manager->_lock.release(); 00452 00453 double start = clock->get_real_time(); 00454 _task_pcollector.start(); 00455 DoneStatus status = do_task(); 00456 _task_pcollector.stop(); 00457 double end = clock->get_real_time(); 00458 00459 // Now reacquire the lock (so we can return with the lock held). 00460 _manager->_lock.acquire(); 00461 00462 _dt = end - start; 00463 _max_dt = max(_dt, _max_dt); 00464 _total_dt += _dt; 00465 00466 _chain->_time_in_frame += _dt; 00467 00468 clear_task(current_thread); 00469 00470 return status; 00471 } 00472 00473 //////////////////////////////////////////////////////////////////// 00474 // Function: AsyncTask::is_runnable 00475 // Access: Protected, Virtual 00476 // Description: Override this function to return true if the task can 00477 // be successfully executed, false if it cannot. Mainly 00478 // intended as a sanity check when attempting to add the 00479 // task to a task manager. 00480 // 00481 // This function is called with the lock held. 00482 //////////////////////////////////////////////////////////////////// 00483 bool AsyncTask:: 00484 is_runnable() { 00485 return true; 00486 } 00487 00488 //////////////////////////////////////////////////////////////////// 00489 // Function: AsyncTask::do_task 00490 // Access: Protected, Virtual 00491 // Description: Override this function to do something useful for the 00492 // task. The return value should be one of: 00493 // 00494 // DS_done: the task is finished, remove from active and 00495 // throw the done event. 00496 // 00497 // DS_cont: the task has more work to do, keep it active 00498 // and call this function again in the next epoch. 00499 // 00500 // DS_again: like DS_cont, but next time call the 00501 // function from the beginning, almost as if it were 00502 // freshly added to the task manager. The task's 00503 // get_start_time() will be reset to now, and its 00504 // get_elapsed_time() will be reset to 0. If the task 00505 // has a set_delay(), it will wait again for that amount 00506 // of time to elapse before restarting. Timing 00507 // accounting, however, is not reset. 00508 // 00509 // DS_pickup: like DS_cont, but if the task chain has a 00510 // frame budget and that budget has not yet been met, 00511 // re-run the task again without waiting for the next 00512 // frame. Otherwise, run it next epoch as usual. 00513 // 00514 // DS_exit: stop the task, and stop the enclosing 00515 // sequence too. Outside of a sequence, this is the 00516 // same as DS_done. 00517 // 00518 // DS_pause: delay the task for set_delay() seconds, 00519 // then stop it. This is only useful within a sequence. 00520 // 00521 // DS_interrupt: Interrupt the whole AsyncTaskManager. 00522 // The task will continue again next epoch, as if it had 00523 // returned DS_cont. 00524 // 00525 // This function is called with the lock *not* held. 00526 //////////////////////////////////////////////////////////////////// 00527 AsyncTask::DoneStatus AsyncTask:: 00528 do_task() { 00529 return DS_done; 00530 } 00531 00532 //////////////////////////////////////////////////////////////////// 00533 // Function: AsyncTask::upon_birth 00534 // Access: Protected, Virtual 00535 // Description: Override this function to do something useful when the 00536 // task has been added to the active queue. 00537 // 00538 // This function is called with the lock *not* held. 00539 //////////////////////////////////////////////////////////////////// 00540 void AsyncTask:: 00541 upon_birth(AsyncTaskManager *manager) { 00542 // Throw a generic add event for the manager. 00543 string add_name = manager->get_name() + "-addTask"; 00544 PT_Event event = new Event(add_name); 00545 event->add_parameter(EventParameter(this)); 00546 throw_event(event); 00547 } 00548 00549 //////////////////////////////////////////////////////////////////// 00550 // Function: AsyncTask::upon_death 00551 // Access: Protected, Virtual 00552 // Description: Override this function to do something useful when the 00553 // task has been removed from the active queue. The 00554 // parameter clean_exit is true if the task has been 00555 // removed because it exited normally (returning 00556 // DS_done), or false if it was removed for some other 00557 // reason (e.g. AsyncTaskManager::remove()). By the 00558 // time this method is called, _manager has been 00559 // cleared, so the parameter manager indicates the 00560 // original AsyncTaskManager that owned this task. 00561 // 00562 // The normal behavior is to throw the done_event only 00563 // if clean_exit is true. 00564 // 00565 // This function is called with the lock *not* held. 00566 //////////////////////////////////////////////////////////////////// 00567 void AsyncTask:: 00568 upon_death(AsyncTaskManager *manager, bool clean_exit) { 00569 if (clean_exit && !_done_event.empty()) { 00570 PT_Event event = new Event(_done_event); 00571 event->add_parameter(EventParameter(this)); 00572 throw_event(event); 00573 } 00574 00575 // Also throw a generic remove event for the manager. 00576 if (manager != (AsyncTaskManager *)NULL) { 00577 string remove_name = manager->get_name() + "-removeTask"; 00578 PT_Event event = new Event(remove_name); 00579 event->add_parameter(EventParameter(this)); 00580 throw_event(event); 00581 } 00582 }