diff --git a/direct/src/task/TaskNew.py b/direct/src/task/TaskNew.py index a30f044140..df71fef3f6 100644 --- a/direct/src/task/TaskNew.py +++ b/direct/src/task/TaskNew.py @@ -3,7 +3,8 @@ AsyncTaskManager interface. It replaces the old full-Python implementation of the Task system. """ __all__ = ['Task', 'TaskManager', - 'cont', 'done', 'again', 'pickup'] + 'cont', 'done', 'again', 'pickup', 'exit', + 'sequence', 'loop', 'pause'] from direct.directnotify.DirectNotifyGlobal import * from direct.showbase import ExceptionVarDump @@ -57,6 +58,7 @@ done = AsyncTask.DSDone cont = AsyncTask.DSCont again = AsyncTask.DSAgain pickup = AsyncTask.DSPickup +exit = AsyncTask.DSExit # Alias PythonTask to Task for historical purposes. Task = PythonTask @@ -68,6 +70,26 @@ Task.DtoolClassDict['done'] = done Task.DtoolClassDict['cont'] = cont Task.DtoolClassDict['again'] = again Task.DtoolClassDict['pickup'] = pickup +Task.DtoolClassDict['exit'] = exit + +# Alias the AsyncTaskPause constructor as Task.pause(). +pause = AsyncTaskPause +Task.DtoolClassDict['pause'] = staticmethod(pause) + +def sequence(*taskList): + seq = AsyncTaskSequence('sequence') + for task in taskList: + seq.addTask(task) + return seq +Task.DtoolClassDict['sequence'] = staticmethod(sequence) + +def loop(*taskList): + seq = AsyncTaskSequence('loop') + for task in taskList: + seq.addTask(task) + seq.setRepeatCount(-1) + return seq +Task.DtoolClassDict['loop'] = staticmethod(loop) class TaskManager: notify = directNotify.newCategory("TaskManager") @@ -273,7 +295,7 @@ class TaskManager: return task def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath): - if isinstance(funcOrTask, PythonTask): + if isinstance(funcOrTask, AsyncTask): task = funcOrTask elif callable(funcOrTask): task = PythonTask(funcOrTask) @@ -281,10 +303,15 @@ class TaskManager: self.notify.error( 'add: Tried to add a task that was not a Task or a func') - if extraArgs is None: - extraArgs = [] - appendTask = True - task.setArgs(extraArgs, appendTask) + if hasattr(task, 'setArgs'): + # It will only accept arguments if it's a PythonTask. + if extraArgs is None: + extraArgs = [] + appendTask = True + task.setArgs(extraArgs, appendTask) + elif extraArgs is not None: + self.notify.error( + 'Task %s does not accept arguments.' % (repr(task))) if name is not None: assert isinstance(name, types.StringTypes), 'Name must be a string type' @@ -301,11 +328,6 @@ class TaskManager: if sort is not None: task.setSort(sort) - if extraArgs is None: - extraArgs = [] - appendTask = True - task.setArgs(extraArgs, appendTask) - if taskChain is not None: task.setTaskChain(taskChain) diff --git a/panda/src/event/Sources.pp b/panda/src/event/Sources.pp index 12516343da..7e1ee2bf04 100644 --- a/panda/src/event/Sources.pp +++ b/panda/src/event/Sources.pp @@ -12,6 +12,8 @@ asyncTaskChain.h asyncTaskChain.I \ asyncTaskCollection.h asyncTaskCollection.I \ asyncTaskManager.h asyncTaskManager.I \ + asyncTaskPause.h asyncTaskPause.I \ + asyncTaskSequence.h asyncTaskSequence.I \ config_event.h \ buttonEvent.I buttonEvent.h \ buttonEventList.I buttonEventList.h \ @@ -29,6 +31,8 @@ asyncTaskChain.cxx \ asyncTaskCollection.cxx \ asyncTaskManager.cxx \ + asyncTaskPause.cxx \ + asyncTaskSequence.cxx \ buttonEvent.cxx \ buttonEventList.cxx \ genericAsyncTask.cxx \ @@ -44,6 +48,8 @@ asyncTaskChain.h asyncTaskChain.I \ asyncTaskCollection.h asyncTaskCollection.I \ asyncTaskManager.h asyncTaskManager.I \ + asyncTaskPause.h asyncTaskPause.I \ + asyncTaskSequence.h asyncTaskSequence.I \ buttonEvent.I buttonEvent.h \ buttonEventList.I buttonEventList.h \ genericAsyncTask.h genericAsyncTask.I \ diff --git a/panda/src/event/asyncTask.cxx b/panda/src/event/asyncTask.cxx index d984f86ad8..cb2204618b 100644 --- a/panda/src/event/asyncTask.cxx +++ b/panda/src/event/asyncTask.cxx @@ -417,7 +417,14 @@ is_runnable() { // re-run the task again without waiting for the next // frame. Otherwise, run it next epoch as usual. // -// DS_abort: abort the task, and interrupt the whole +// DS_exit: stop the task, and stop the enclosing +// sequence too. Outside of a sequence, this is the +// same as DS_done. +// +// DS_pause: delay the task for set_delay() seconds, +// then stop it. This is only useful within a sequence. +// +// DS_abort: stop the task, and interrupt the whole // AsyncTaskManager. // // This function is called with the lock *not* held. @@ -433,9 +440,7 @@ do_task() { // Description: Override this function to do something useful when the // task has been added to the active queue. // -// This function is called with the lock held. You may -// temporarily release if it necessary, but be sure to -// return with it held. +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// void AsyncTask:: upon_birth() { @@ -454,9 +459,7 @@ upon_birth() { // The normal behavior is to throw the done_event only // if clean_exit is true. // -// This function is called with the lock held. You may -// temporarily release if it necessary, but be sure to -// return with it held. +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// void AsyncTask:: upon_death(bool clean_exit) { @@ -466,30 +469,3 @@ upon_death(bool clean_exit) { throw_event(event); } } - -//////////////////////////////////////////////////////////////////// -// Function: AsyncTask::release_lock -// Access: Protected -// Description: Releases the task lock. This is intended to be -// used within callback functions, for instance -// upon_birth() or upon_death(), as needed to release -// the lock for processing. Be sure to grab it again -// before returning. -//////////////////////////////////////////////////////////////////// -void AsyncTask:: -release_lock() { - _manager->_lock.release(); -} - -//////////////////////////////////////////////////////////////////// -// Function: AsyncTask::grab_lock -// Access: Protected -// Description: Releases the task lock. This is intended to be -// used within callback functions, to grab the lock -// after a previous call to release_lock(). -//////////////////////////////////////////////////////////////////// -void AsyncTask:: -grab_lock() { - _manager->_lock.lock(); -} - diff --git a/panda/src/event/asyncTask.h b/panda/src/event/asyncTask.h index f0c4ff113e..3dfa6cb8a4 100644 --- a/panda/src/event/asyncTask.h +++ b/panda/src/event/asyncTask.h @@ -44,16 +44,19 @@ class AsyncTaskChain; class EXPCL_PANDA_EVENT AsyncTask : public TypedReferenceCount, public Namable { public: AsyncTask(const string &name = string()); - virtual ~AsyncTask(); ALLOC_DELETED_CHAIN(AsyncTask); PUBLISHED: + virtual ~AsyncTask(); + enum DoneStatus { DS_done, // normal task completion DS_cont, // run task again next epoch DS_again, // start the task over from the beginning DS_pickup, // run task again this frame, if frame budget allows - DS_abort, // abort the task and interrupt the whole task manager + DS_exit, // stop the enclosing sequence + DS_pause, // pause, then exit (useful within a sequence) + DS_abort, // interrupt the task manager }; enum State { @@ -62,6 +65,7 @@ PUBLISHED: S_servicing, S_servicing_removed, // Still servicing, but wants removal from manager. S_sleeping, + S_active_nested, // active within a sequence. }; INLINE State get_state() const; @@ -117,9 +121,6 @@ protected: virtual void upon_birth(); virtual void upon_death(bool clean_exit); - void release_lock(); - void grab_lock(); - protected: AtomicAdjust::Integer _task_id; string _chain_name; @@ -172,6 +173,7 @@ private: friend class AsyncTaskManager; friend class AsyncTaskChain; + friend class AsyncTaskSequence; }; INLINE ostream &operator << (ostream &out, const AsyncTask &task) { diff --git a/panda/src/event/asyncTaskChain.cxx b/panda/src/event/asyncTaskChain.cxx index d29b4c35d0..d2efbaeaa9 100644 --- a/panda/src/event/asyncTaskChain.cxx +++ b/panda/src/event/asyncTaskChain.cxx @@ -494,7 +494,8 @@ do_add(AsyncTask *task) { // Access: Protected // Description: Removes the indicated task from this chain. Returns // true if removed, false otherwise. Assumes the lock -// is already held. +// is already held. The task->upon_death() method is +// *not* called. //////////////////////////////////////////////////////////////////// bool AsyncTaskChain:: do_remove(AsyncTask *task) { @@ -521,7 +522,7 @@ do_remove(AsyncTask *task) { _sleeping.erase(_sleeping.begin() + index); make_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime()); removed = true; - cleanup_task(task); + cleanup_task(task, false, false); } break; @@ -542,7 +543,7 @@ do_remove(AsyncTask *task) { } } removed = true; - cleanup_task(task); + cleanup_task(task, false, false); } } @@ -607,23 +608,19 @@ do_cleanup() { TaskHeap::const_iterator ti; for (ti = active.begin(); ti != active.end(); ++ti) { AsyncTask *task = (*ti); - cleanup_task(task); - task->upon_death(false); + cleanup_task(task, true, false); } for (ti = this_active.begin(); ti != this_active.end(); ++ti) { AsyncTask *task = (*ti); - cleanup_task(task); - task->upon_death(false); + cleanup_task(task, true, false); } for (ti = next_active.begin(); ti != next_active.end(); ++ti) { AsyncTask *task = (*ti); - cleanup_task(task); - task->upon_death(false); + cleanup_task(task, true, false); } for (ti = sleeping.begin(); ti != sleeping.end(); ++ti) { AsyncTask *task = (*ti); - cleanup_task(task); - task->upon_death(false); + cleanup_task(task, true, false); } } @@ -706,13 +703,12 @@ service_one_task(AsyncTaskChain::AsyncTaskChainThread *thread) { if (task->_chain == this) { if (task->_state == AsyncTask::S_servicing_removed) { // This task wants to kill itself. - cleanup_task(task); - task->upon_death(false); + cleanup_task(task, true, false); } else if (task->_chain_name != get_name()) { // The task wants to jump to a different chain. PT(AsyncTask) hold_task = task; - cleanup_task(task); + cleanup_task(task, false, false); task->jump_to_task_chain(_manager); } else { @@ -752,8 +748,7 @@ service_one_task(AsyncTaskChain::AsyncTaskChainThread *thread) { case AsyncTask::DS_abort: // The task had an exception and wants to raise a big flag. - cleanup_task(task); - task->upon_death(false); + cleanup_task(task, true, false); if (_state == S_started) { _state = S_aborting; _cvar.signal_all(); @@ -762,8 +757,7 @@ service_one_task(AsyncTaskChain::AsyncTaskChainThread *thread) { default: // The task has finished. - cleanup_task(task); - task->upon_death(true); + cleanup_task(task, true, true); } } } @@ -777,11 +771,15 @@ service_one_task(AsyncTaskChain::AsyncTaskChainThread *thread) { // interrupted) and is about to be removed from the // active queue. Assumes the lock is held. // +// If upon_death is true, then task->upon_death() will +// also be called, with the indicated clean_exit +// parameter. +// // Note that the lock may be temporarily released by // this method. //////////////////////////////////////////////////////////////////// void AsyncTaskChain:: -cleanup_task(AsyncTask *task) { +cleanup_task(AsyncTask *task, bool upon_death, bool clean_exit) { nassertv(task->_chain == this); PT(AsyncTask) hold_task = task; @@ -792,6 +790,12 @@ cleanup_task(AsyncTask *task) { --(_manager->_num_tasks); _manager->remove_task_by_name(task); + + if (upon_death) { + _manager->_lock.release(); + task->upon_death(clean_exit); + _manager->_lock.lock(); + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/event/asyncTaskChain.h b/panda/src/event/asyncTaskChain.h index 1ea81a2fe5..110f87945d 100644 --- a/panda/src/event/asyncTaskChain.h +++ b/panda/src/event/asyncTaskChain.h @@ -109,7 +109,7 @@ protected: int find_task_on_heap(const TaskHeap &heap, AsyncTask *task) const; void service_one_task(AsyncTaskChainThread *thread); - void cleanup_task(AsyncTask *task); + void cleanup_task(AsyncTask *task, bool upon_death, bool clean_exit); bool finish_sort_group(); void filter_timeslice_priority(); void do_stop_threads(); diff --git a/panda/src/event/asyncTaskCollection.cxx b/panda/src/event/asyncTaskCollection.cxx index aae7f88b66..03523d07a6 100644 --- a/panda/src/event/asyncTaskCollection.cxx +++ b/panda/src/event/asyncTaskCollection.cxx @@ -13,7 +13,6 @@ //////////////////////////////////////////////////////////////////// #include "asyncTaskCollection.h" - #include "indent.h" //////////////////////////////////////////////////////////////////// diff --git a/panda/src/event/asyncTaskCollection.h b/panda/src/event/asyncTaskCollection.h index 178e20eeb7..c3f08bc7c4 100644 --- a/panda/src/event/asyncTaskCollection.h +++ b/panda/src/event/asyncTaskCollection.h @@ -21,7 +21,11 @@ //////////////////////////////////////////////////////////////////// // Class : AsyncTaskCollection -// Description : +// Description : A list of tasks, for instance as returned by some of +// the AsyncTaskManager query functions. This also +// serves to define an AsyncTaskSequence. +// +// TODO: None of this is thread-safe yet. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_PGRAPH AsyncTaskCollection { PUBLISHED: diff --git a/panda/src/event/asyncTaskManager.cxx b/panda/src/event/asyncTaskManager.cxx index 1026f992b9..3b87a8e3f5 100644 --- a/panda/src/event/asyncTaskManager.cxx +++ b/panda/src/event/asyncTaskManager.cxx @@ -199,34 +199,37 @@ void AsyncTaskManager:: add(AsyncTask *task) { nassertv(task->is_runnable()); - MutexHolder holder(_lock); - - if (task_cat.is_debug()) { - task_cat.debug() - << "Adding " << *task << "\n"; - } - - if (task->_state == AsyncTask::S_servicing_removed) { - if (task->_manager == this) { - // Re-adding a self-removed task; this just means clearing the - // removed flag. - task->_state = AsyncTask::S_servicing; - return; + { + MutexHolder holder(_lock); + + if (task_cat.is_debug()) { + task_cat.debug() + << "Adding " << *task << "\n"; } + + if (task->_state == AsyncTask::S_servicing_removed) { + if (task->_manager == this) { + // Re-adding a self-removed task; this just means clearing the + // removed flag. + task->_state = AsyncTask::S_servicing; + return; + } + } + + nassertv(task->_manager == NULL && + task->_state == AsyncTask::S_inactive); + nassertv(!do_has_task(task)); + + AsyncTaskChain *chain = do_find_task_chain(task->_chain_name); + if (chain == (AsyncTaskChain *)NULL) { + task_cat.warning() + << "Creating implicit AsyncTaskChain " << task->_chain_name + << " for " << get_type() << " " << get_name() << "\n"; + chain = do_make_task_chain(task->_chain_name); + } + chain->do_add(task); } - nassertv(task->_manager == NULL && - task->_state == AsyncTask::S_inactive); - nassertv(!do_has_task(task)); - - AsyncTaskChain *chain = do_find_task_chain(task->_chain_name); - if (chain == (AsyncTaskChain *)NULL) { - task_cat.warning() - << "Creating implicit AsyncTaskChain " << task->_chain_name - << " for " << get_type() << " " << get_name() << "\n"; - chain = do_make_task_chain(task->_chain_name); - } - chain->do_add(task); task->upon_birth(); } @@ -353,7 +356,7 @@ remove(const AsyncTaskCollection &tasks) { int num_tasks = tasks.get_num_tasks(); int i; for (i = 0; i < num_tasks; ++i) { - AsyncTask *task = tasks.get_task(i); + PT(AsyncTask) task = tasks.get_task(i); if (task->_manager != this) { // Not a member of this manager, or already removed. @@ -365,7 +368,9 @@ remove(const AsyncTaskCollection &tasks) { << "Removing " << *task << "\n"; } if (task->_chain->do_remove(task)) { + _lock.release(); task->upon_death(false); + _lock.lock(); ++num_removed; } else { if (task_cat.is_debug()) { diff --git a/panda/src/event/asyncTaskManager.h b/panda/src/event/asyncTaskManager.h index ab2cd646d5..445b05cefd 100644 --- a/panda/src/event/asyncTaskManager.h +++ b/panda/src/event/asyncTaskManager.h @@ -156,6 +156,7 @@ private: friend class AsyncTaskChain; friend class AsyncTaskChain::AsyncTaskChainThread; friend class AsyncTask; + friend class AsyncTaskSequence; }; INLINE ostream &operator << (ostream &out, const AsyncTaskManager &manager) { diff --git a/panda/src/event/asyncTaskPause.I b/panda/src/event/asyncTaskPause.I new file mode 100644 index 0000000000..33e46c4717 --- /dev/null +++ b/panda/src/event/asyncTaskPause.I @@ -0,0 +1,14 @@ +// Filename: asyncTaskPause.I +// Created by: drose (04Oct08) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/event/asyncTaskPause.cxx b/panda/src/event/asyncTaskPause.cxx new file mode 100644 index 0000000000..3a7367e950 --- /dev/null +++ b/panda/src/event/asyncTaskPause.cxx @@ -0,0 +1,42 @@ +// Filename: asyncTaskPause.cxx +// Created by: drose (04Oct08) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "asyncTaskPause.h" + +TypeHandle AsyncTaskPause::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskPause::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +AsyncTaskPause:: +AsyncTaskPause(double delay) : + AsyncTask("pause") +{ + set_delay(delay); +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskPause::do_task +// Access: Protected, Virtual +// Description: Override this function to do something useful for the +// task. +// +// This function is called with the lock *not* held. +//////////////////////////////////////////////////////////////////// +AsyncTask::DoneStatus AsyncTaskPause:: +do_task() { + return DS_pause; +} diff --git a/panda/src/event/asyncTaskPause.h b/panda/src/event/asyncTaskPause.h new file mode 100644 index 0000000000..5106a3a3f1 --- /dev/null +++ b/panda/src/event/asyncTaskPause.h @@ -0,0 +1,60 @@ +// Filename: asyncTaskPause.h +// Created by: drose (04Oct08) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ASYNCTASKPAUSE_H +#define ASYNCTASKPAUSE_H + +#include "pandabase.h" + +#include "asyncTask.h" + +class AsyncTaskManager; + +//////////////////////////////////////////////////////////////////// +// Class : AsyncTaskPause +// Description : A special kind of task that simple returns DS_pause, +// to pause for a specified number of seconds and then +// finish. It's intended to be used within an +// AsyncTaskSequence. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA_EVENT AsyncTaskPause : public AsyncTask { +PUBLISHED: + AsyncTaskPause(double delay); + ALLOC_DELETED_CHAIN(AsyncTaskPause); + +protected: + virtual DoneStatus do_task(); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AsyncTask::init_type(); + register_type(_type_handle, "AsyncTaskPause", + AsyncTask::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "asyncTaskPause.I" + +#endif + diff --git a/panda/src/event/asyncTaskSequence.I b/panda/src/event/asyncTaskSequence.I new file mode 100644 index 0000000000..b11730aba6 --- /dev/null +++ b/panda/src/event/asyncTaskSequence.I @@ -0,0 +1,51 @@ +// Filename: asyncTaskSequence.I +// Created by: drose (04Oct08) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::set_repeat_count +// Access: Published +// Description: Sets the repeat count of the sequence. If the count +// is 0 or 1, the sequence will run exactly once. If it +// is greater than 0, it will run that number of times. +// If it is negative, it will run forever until it is +// explicitly removed. +//////////////////////////////////////////////////////////////////// +INLINE void AsyncTaskSequence:: +set_repeat_count(int repeat_count) { + _repeat_count = repeat_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::get_repeat_count +// Access: Published +// Description: Returns the repeat count of the sequence. See +// set_repeat_count(). +//////////////////////////////////////////////////////////////////// +INLINE int AsyncTaskSequence:: +get_repeat_count() const { + return _repeat_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::get_current_task_index +// Access: Published +// Description: Returns the index of the task within the sequence +// that is currently being executed (or that will be +// executed at the next epoch). +//////////////////////////////////////////////////////////////////// +INLINE int AsyncTaskSequence:: +get_current_task_index() const { + return _task_index; +} diff --git a/panda/src/event/asyncTaskSequence.cxx b/panda/src/event/asyncTaskSequence.cxx new file mode 100644 index 0000000000..75b23a62da --- /dev/null +++ b/panda/src/event/asyncTaskSequence.cxx @@ -0,0 +1,193 @@ +// Filename: asyncTaskSequence.cxx +// Created by: drose (04Oct08) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "asyncTaskSequence.h" + +TypeHandle AsyncTaskSequence::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +AsyncTaskSequence:: +AsyncTaskSequence(const string &name) : + AsyncTask(name), + _repeat_count(0), + _task_index(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::Destructor +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +AsyncTaskSequence:: +~AsyncTaskSequence() { + set_current_task(NULL, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::is_runnable +// Access: Protected, Virtual +// Description: Override this function to return true if the task can +// be successfully executed, false if it cannot. Mainly +// intended as a sanity check when attempting to add the +// task to a task manager. +// +// This function is called with the lock held. +//////////////////////////////////////////////////////////////////// +bool AsyncTaskSequence:: +is_runnable() { + return (get_num_tasks() > 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::do_task +// Access: Protected, Virtual +// Description: Override this function to do something useful for the +// task. +// +// This function is called with the lock *not* held. +//////////////////////////////////////////////////////////////////// +AsyncTask::DoneStatus AsyncTaskSequence:: +do_task() { + // Clear the delay that might have been set from a previous wait. + _delay = 0.0; + _has_delay = false; + + if (_task_index >= get_num_tasks()) { + // Ran off the end of the task list. + set_current_task(NULL, true); + _task_index = 0; + if (_task_index >= get_num_tasks()) { + return DS_done; + } + if (_repeat_count > 0) { + --_repeat_count; + } + if (_repeat_count != 0) { + return DS_cont; + } + return DS_done; + } + + AsyncTask *task = get_task(_task_index); + set_current_task(task, true); + nassertr(_current_task != (AsyncTask *)NULL, DS_exit); + + DoneStatus result = _current_task->do_task(); + switch (result) { + case DS_again: + case DS_pause: + // The task wants to sleep for a period of time. + { + double now = _manager->_clock->get_frame_time(); + _current_task->_start_time = now + _current_task->_delay; + + _delay = _current_task->_delay; + _has_delay = _current_task->_has_delay; + + if (result == DS_pause) { + // When it wakes up, move on to the next task. + ++_task_index; + } + } + return DS_again; + + case DS_done: + // Go on to the next task. + ++_task_index; + return DS_cont; + + case DS_cont: + case DS_pickup: + case DS_exit: + case DS_abort: + // Just return these results through. + return result; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::upon_birth +// Access: Protected, Virtual +// Description: Override this function to do something useful when the +// task has been added to the active queue. +// +// This function is called with the lock held. You may +// temporarily release if it necessary, but be sure to +// return with it held. +//////////////////////////////////////////////////////////////////// +void AsyncTaskSequence:: +upon_birth() { + AsyncTask::upon_birth(); + _task_index = 0; + set_current_task(NULL, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::upon_death +// Access: Protected, Virtual +// Description: Override this function to do something useful when the +// task has been removed from the active queue. The +// parameter clean_exit is true if the task has been +// removed because it exited normally (returning +// DS_done), or false if it was removed for some other +// reason (e.g. AsyncTaskManager::remove()). +// +// The normal behavior is to throw the done_event only +// if clean_exit is true. +// +// This function is called with the lock held. You may +// temporarily release if it necessary, but be sure to +// return with it held. +//////////////////////////////////////////////////////////////////// +void AsyncTaskSequence:: +upon_death(bool clean_exit) { + AsyncTask::upon_death(clean_exit); + set_current_task(NULL, clean_exit); +} + +//////////////////////////////////////////////////////////////////// +// Function: AsyncTaskSequence::set_current_task +// Access: Private +// Description: Sets the current executing task. If NULL, sets the +// current task to NULL. +//////////////////////////////////////////////////////////////////// +void AsyncTaskSequence:: +set_current_task(AsyncTask *task, bool clean_exit) { + if (_current_task == task) { + return; + } + + if (_current_task != (AsyncTask *)NULL) { + nassertv(_current_task->_state == S_active_nested); + nassertv(_current_task->_manager == _manager || _manager == NULL); + _current_task->_state = S_inactive; + _current_task->upon_death(clean_exit); + _current_task->_manager = NULL; + } + + _current_task = task; + + if (_current_task != (AsyncTask *)NULL) { + nassertv(_current_task->_state == S_inactive); + nassertv(_current_task->_manager == NULL); + _current_task->_manager = _manager; + _current_task->_state = S_active_nested; + _current_task->upon_birth(); + } +} diff --git a/panda/src/event/asyncTaskSequence.h b/panda/src/event/asyncTaskSequence.h new file mode 100644 index 0000000000..d89aa69376 --- /dev/null +++ b/panda/src/event/asyncTaskSequence.h @@ -0,0 +1,82 @@ +// Filename: asyncTaskSequence.h +// Created by: drose (04Oct08) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef ASYNCTASKSEQUENCE_H +#define ASYNCTASKSEQUENCE_H + +#include "pandabase.h" + +#include "asyncTask.h" +#include "asyncTaskCollection.h" + +class AsyncTaskManager; + +//////////////////////////////////////////////////////////////////// +// Class : AsyncTaskSequence +// Description : A special kind of task that serves as a list of tasks +// internally. Each task on the list is executed in +// sequence, one per epoch. +// +// This is similar to a Sequence interval, though it has +// some slightly different abilities. For instance, +// although you can't start at any arbitrary point in +// the sequence, you can construct a task sequence whose +// duration changes during playback. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA_EVENT AsyncTaskSequence : public AsyncTask, public AsyncTaskCollection { +PUBLISHED: + AsyncTaskSequence(const string &name); + virtual ~AsyncTaskSequence(); + ALLOC_DELETED_CHAIN(AsyncTaskSequence); + + INLINE void set_repeat_count(int repeat_count); + INLINE int get_repeat_count() const; + + INLINE int get_current_task_index() const; + +protected: + virtual bool is_runnable(); + virtual DoneStatus do_task(); + virtual void upon_birth(); + virtual void upon_death(bool clean_exit); + +private: + void set_current_task(AsyncTask *task, bool clean_exit); + + int _repeat_count; + int _task_index; + PT(AsyncTask) _current_task; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AsyncTask::init_type(); + register_type(_type_handle, "AsyncTaskSequence", + AsyncTask::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "asyncTaskSequence.I" + +#endif + diff --git a/panda/src/event/config_event.cxx b/panda/src/event/config_event.cxx index 221c4a1c40..202fa44b80 100644 --- a/panda/src/event/config_event.cxx +++ b/panda/src/event/config_event.cxx @@ -16,6 +16,8 @@ #include "asyncTask.h" #include "asyncTaskChain.h" #include "asyncTaskManager.h" +#include "asyncTaskPause.h" +#include "asyncTaskSequence.h" #include "buttonEventList.h" #include "event.h" #include "eventHandler.h" @@ -34,6 +36,8 @@ ConfigureFn(config_event) { AsyncTask::init_type(); AsyncTaskChain::init_type(); AsyncTaskManager::init_type(); + AsyncTaskPause::init_type(); + AsyncTaskSequence::init_type(); ButtonEventList::init_type(); PointerEventList::init_type(); Event::init_type(); diff --git a/panda/src/event/event_composite1.cxx b/panda/src/event/event_composite1.cxx index d3e992264a..08165b1800 100644 --- a/panda/src/event/event_composite1.cxx +++ b/panda/src/event/event_composite1.cxx @@ -2,6 +2,8 @@ #include "asyncTaskChain.cxx" #include "asyncTaskCollection.cxx" #include "asyncTaskManager.cxx" +#include "asyncTaskPause.cxx" +#include "asyncTaskSequence.cxx" #include "buttonEvent.cxx" #include "buttonEventList.cxx" #include "genericAsyncTask.cxx" diff --git a/panda/src/event/genericAsyncTask.cxx b/panda/src/event/genericAsyncTask.cxx index f3577161ed..adc21c176f 100644 --- a/panda/src/event/genericAsyncTask.cxx +++ b/panda/src/event/genericAsyncTask.cxx @@ -65,7 +65,10 @@ is_runnable() { //////////////////////////////////////////////////////////////////// // Function: GenericAsyncTask::do_task // Access: Protected, Virtual -// Description: +// Description: Override this function to do something useful for the +// task. +// +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// AsyncTask::DoneStatus GenericAsyncTask:: do_task() { @@ -79,18 +82,14 @@ do_task() { // Description: Override this function to do something useful when the // task has been added to the active queue. // -// This function is called with the lock held. You may -// temporarily release if it necessary, but be sure to -// return with it held. +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// void GenericAsyncTask:: upon_birth() { AsyncTask::upon_birth(); if (_upon_birth != NULL) { - release_lock(); (*_upon_birth)(this, _user_data); - grab_lock(); } } @@ -107,17 +106,13 @@ upon_birth() { // The normal behavior is to throw the done_event only // if clean_exit is true. // -// This function is called with the lock held. You may -// temporarily release if it necessary, but be sure to -// return with it held. +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// void GenericAsyncTask:: upon_death(bool clean_exit) { AsyncTask::upon_death(clean_exit); if (_upon_death != NULL) { - release_lock(); (*_upon_death)(this, clean_exit, _user_data); - grab_lock(); } } diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 312b036a1e..55328c0b4f 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -341,7 +341,10 @@ is_runnable() { //////////////////////////////////////////////////////////////////// // Function: PythonTask::do_task // Access: Protected, Virtual -// Description: +// Description: Override this function to do something useful for the +// task. +// +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// AsyncTask::DoneStatus PythonTask:: do_task() { @@ -434,6 +437,8 @@ do_python_task() { case DS_done: case DS_cont: case DS_pickup: + case DS_exit: + case DS_pause: // Legitimate value. Py_DECREF(result); return (DoneStatus)retval; @@ -467,16 +472,13 @@ do_python_task() { // Description: Override this function to do something useful when the // task has been added to the active queue. // -// This function is called with the lock held. You may -// temporarily release if it necessary, but be sure to -// return with it held. +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// void PythonTask:: upon_birth() { AsyncTask::upon_birth(); if (_owner != Py_None) { - release_lock(); #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) // Use PyGILState to protect this asynchronous call. PyGILState_STATE gstate; @@ -488,7 +490,6 @@ upon_birth() { #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) PyGILState_Release(gstate); #endif - grab_lock(); } } @@ -505,16 +506,13 @@ upon_birth() { // The normal behavior is to throw the done_event only // if clean_exit is true. // -// This function is called with the lock held. You may -// temporarily release if it necessary, but be sure to -// return with it held. +// This function is called with the lock *not* held. //////////////////////////////////////////////////////////////////// void PythonTask:: upon_death(bool clean_exit) { AsyncTask::upon_death(clean_exit); if (_owner != Py_None && _upon_death != Py_None) { - release_lock(); #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) // Use PyGILState to protect this asynchronous call. PyGILState_STATE gstate; @@ -527,7 +525,6 @@ upon_death(bool clean_exit) { #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) PyGILState_Release(gstate); #endif - grab_lock(); } }