Panda3D
 All Classes Functions Variables Enumerations
asyncTask.cxx
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 }
 All Classes Functions Variables Enumerations