Task.sequence()

This commit is contained in:
David Rose 2008-10-05 00:24:55 +00:00
parent 6599417ca1
commit a6a4a893d0
20 changed files with 579 additions and 120 deletions

View File

@ -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)

View File

@ -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 \

View File

@ -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();
}

View File

@ -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) {

View File

@ -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();
}
}
////////////////////////////////////////////////////////////////////

View File

@ -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();

View File

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

View File

@ -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:

View File

@ -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()) {

View File

@ -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) {

View File

@ -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."
//
////////////////////////////////////////////////////////////////////

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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();

View File

@ -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"

View File

@ -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();
}
}

View File

@ -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();
}
}