Browse Source

Task.sequence()

David Rose 17 years ago
parent
commit
a6a4a893d0

+ 33 - 11
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)
 

+ 6 - 0
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 \

+ 10 - 34
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();
-}
-

+ 7 - 5
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) {

+ 23 - 19
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();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
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();

+ 0 - 1
panda/src/event/asyncTaskCollection.cxx

@@ -13,7 +13,6 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "asyncTaskCollection.h"
-
 #include "indent.h"
 
 ////////////////////////////////////////////////////////////////////

+ 5 - 1
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:

+ 31 - 26
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()) {

+ 1 - 0
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) {

+ 14 - 0
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."
+//
+////////////////////////////////////////////////////////////////////
+

+ 42 - 0
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;
+}

+ 60 - 0
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
+

+ 51 - 0
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;
+}

+ 193 - 0
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();
+  }
+}

+ 82 - 0
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
+

+ 4 - 0
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();

+ 2 - 0
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"

+ 6 - 11
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();
   }
 }

+ 8 - 11
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();
   }
 }