231 lines
6.7 KiB
C
231 lines
6.7 KiB
C
|
/**
|
||
|
* 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."
|
||
|
*
|
||
|
* @file asyncTaskChain.h
|
||
|
* @author drose
|
||
|
* @date 2006-08-23
|
||
|
*/
|
||
|
|
||
|
#ifndef ASYNCTASKCHAIN_H
|
||
|
#define ASYNCTASKCHAIN_H
|
||
|
|
||
|
#include "pandabase.h"
|
||
|
|
||
|
#include "asyncTask.h"
|
||
|
#include "asyncTaskCollection.h"
|
||
|
#include "typedReferenceCount.h"
|
||
|
#include "thread.h"
|
||
|
#include "conditionVar.h"
|
||
|
#include "pvector.h"
|
||
|
#include "pdeque.h"
|
||
|
#include "pStatCollector.h"
|
||
|
#include "clockObject.h"
|
||
|
|
||
|
class AsyncTaskManager;
|
||
|
|
||
|
/**
|
||
|
* The AsyncTaskChain is a subset of the AsyncTaskManager. Each chain
|
||
|
* maintains a separate list of tasks, and will execute them with its own set
|
||
|
* of threads. Each chain may thereby operate independently of the other
|
||
|
* chains.
|
||
|
*
|
||
|
* The AsyncTaskChain will spawn a specified number of threads (possibly 0) to
|
||
|
* serve the tasks. If there are no threads, you must call poll() from time
|
||
|
* to time to serve the tasks in the main thread. Normally this is done by
|
||
|
* calling AsyncTaskManager::poll().
|
||
|
*
|
||
|
* Each task will run exactly once each epoch. Beyond that, the tasks' sort
|
||
|
* and priority values control the order in which they are run: tasks are run
|
||
|
* in increasing order by sort value, and within the same sort value, they are
|
||
|
* run roughly in decreasing order by priority value, with some exceptions for
|
||
|
* parallelism. Tasks with different sort values are never run in parallel
|
||
|
* together, but tasks with different priority values might be (if there is
|
||
|
* more than one thread).
|
||
|
*/
|
||
|
class EXPCL_PANDA_EVENT AsyncTaskChain : public TypedReferenceCount, public Namable {
|
||
|
public:
|
||
|
AsyncTaskChain(AsyncTaskManager *manager, const std::string &name);
|
||
|
~AsyncTaskChain();
|
||
|
|
||
|
PUBLISHED:
|
||
|
void set_tick_clock(bool tick_clock);
|
||
|
bool get_tick_clock() const;
|
||
|
|
||
|
BLOCKING void set_num_threads(int num_threads);
|
||
|
int get_num_threads() const;
|
||
|
int get_num_running_threads() const;
|
||
|
|
||
|
BLOCKING void set_thread_priority(ThreadPriority priority);
|
||
|
ThreadPriority get_thread_priority() const;
|
||
|
|
||
|
void set_frame_budget(double frame_budget);
|
||
|
double get_frame_budget() const;
|
||
|
|
||
|
void set_frame_sync(bool frame_sync);
|
||
|
bool get_frame_sync() const;
|
||
|
|
||
|
void set_timeslice_priority(bool timeslice_priority);
|
||
|
bool get_timeslice_priority() const;
|
||
|
|
||
|
BLOCKING void stop_threads();
|
||
|
void start_threads();
|
||
|
INLINE bool is_started() const;
|
||
|
|
||
|
bool has_task(AsyncTask *task) const;
|
||
|
|
||
|
BLOCKING void wait_for_tasks();
|
||
|
|
||
|
int get_num_tasks() const;
|
||
|
AsyncTaskCollection get_tasks() const;
|
||
|
AsyncTaskCollection get_active_tasks() const;
|
||
|
AsyncTaskCollection get_sleeping_tasks() const;
|
||
|
|
||
|
void poll();
|
||
|
double get_next_wake_time() const;
|
||
|
|
||
|
virtual void output(std::ostream &out) const;
|
||
|
virtual void write(std::ostream &out, int indent_level = 0) const;
|
||
|
|
||
|
protected:
|
||
|
class AsyncTaskChainThread;
|
||
|
typedef pvector< PT(AsyncTask) > TaskHeap;
|
||
|
|
||
|
void do_add(AsyncTask *task);
|
||
|
bool do_remove(AsyncTask *task, bool upon_death=false);
|
||
|
void do_wait_for_tasks();
|
||
|
void do_cleanup();
|
||
|
|
||
|
bool do_has_task(AsyncTask *task) const;
|
||
|
int find_task_on_heap(const TaskHeap &heap, AsyncTask *task) const;
|
||
|
|
||
|
void service_one_task(AsyncTaskChainThread *thread);
|
||
|
void cleanup_task(AsyncTask *task, bool upon_death, bool clean_exit);
|
||
|
bool finish_sort_group();
|
||
|
void filter_timeslice_priority();
|
||
|
void do_stop_threads();
|
||
|
void do_start_threads();
|
||
|
AsyncTaskCollection do_get_active_tasks() const;
|
||
|
AsyncTaskCollection do_get_sleeping_tasks() const;
|
||
|
void do_poll();
|
||
|
void cleanup_pickup_mode();
|
||
|
INLINE double do_get_next_wake_time() const;
|
||
|
static INLINE double get_wake_time(AsyncTask *task);
|
||
|
void do_output(std::ostream &out) const;
|
||
|
void do_write(std::ostream &out, int indent_level) const;
|
||
|
|
||
|
void write_task_line(std::ostream &out, int indent_level, AsyncTask *task, double now) const;
|
||
|
|
||
|
protected:
|
||
|
class AsyncTaskChainThread : public Thread {
|
||
|
public:
|
||
|
AsyncTaskChainThread(const std::string &name, AsyncTaskChain *chain);
|
||
|
virtual void thread_main();
|
||
|
|
||
|
AsyncTaskChain *_chain;
|
||
|
AsyncTask *_servicing;
|
||
|
};
|
||
|
|
||
|
class AsyncTaskSortWakeTime {
|
||
|
public:
|
||
|
bool operator () (AsyncTask *a, AsyncTask *b) const {
|
||
|
return AsyncTaskChain::get_wake_time(a) > AsyncTaskChain::get_wake_time(b);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class AsyncTaskSortPriority {
|
||
|
public:
|
||
|
bool operator () (AsyncTask *a, AsyncTask *b) const {
|
||
|
if (a->get_sort() != b->get_sort()) {
|
||
|
return a->get_sort() > b->get_sort();
|
||
|
}
|
||
|
if (a->get_priority() != b->get_priority()) {
|
||
|
return a->get_priority() < b->get_priority();
|
||
|
}
|
||
|
if (a->get_start_time() != b->get_start_time()) {
|
||
|
return a->get_start_time() > b->get_start_time();
|
||
|
}
|
||
|
// Failing any other ordering criteria, we sort the tasks based on the
|
||
|
// order in which they were added to the task chain.
|
||
|
return a->_implicit_sort > b->_implicit_sort;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
typedef pvector< PT(AsyncTaskChainThread) > Threads;
|
||
|
|
||
|
AsyncTaskManager *_manager;
|
||
|
|
||
|
ConditionVar _cvar; // signaled when one of the task heaps, _state, or _current_sort changes, or a task finishes.
|
||
|
|
||
|
enum State {
|
||
|
S_initial, // no threads yet
|
||
|
S_started, // threads have been started
|
||
|
S_interrupted, // task returned DS_interrupt, requested from sub-thread.
|
||
|
S_shutdown // waiting for thread shutdown, requested from main thread
|
||
|
};
|
||
|
|
||
|
bool _tick_clock;
|
||
|
bool _timeslice_priority;
|
||
|
int _num_threads;
|
||
|
ThreadPriority _thread_priority;
|
||
|
Threads _threads;
|
||
|
double _frame_budget;
|
||
|
bool _frame_sync;
|
||
|
int _num_busy_threads;
|
||
|
int _num_tasks;
|
||
|
int _num_awaiting_tasks;
|
||
|
TaskHeap _active;
|
||
|
TaskHeap _this_active;
|
||
|
TaskHeap _next_active;
|
||
|
TaskHeap _sleeping;
|
||
|
State _state;
|
||
|
int _current_sort;
|
||
|
bool _pickup_mode;
|
||
|
bool _needs_cleanup;
|
||
|
|
||
|
int _current_frame;
|
||
|
double _time_in_frame;
|
||
|
bool _block_till_next_frame;
|
||
|
|
||
|
unsigned int _next_implicit_sort;
|
||
|
|
||
|
static PStatCollector _task_pcollector;
|
||
|
static PStatCollector _wait_pcollector;
|
||
|
|
||
|
public:
|
||
|
static TypeHandle get_class_type() {
|
||
|
return _type_handle;
|
||
|
}
|
||
|
static void init_type() {
|
||
|
TypedReferenceCount::init_type();
|
||
|
register_type(_type_handle, "AsyncTaskChain",
|
||
|
TypedReferenceCount::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;
|
||
|
|
||
|
friend class AsyncFuture;
|
||
|
friend class AsyncTaskChainThread;
|
||
|
friend class AsyncTask;
|
||
|
friend class AsyncTaskManager;
|
||
|
friend class AsyncTaskSortWakeTime;
|
||
|
};
|
||
|
|
||
|
INLINE std::ostream &operator << (std::ostream &out, const AsyncTaskChain &chain) {
|
||
|
chain.output(out);
|
||
|
return out;
|
||
|
};
|
||
|
|
||
|
#include "asyncTaskChain.I"
|
||
|
|
||
|
#endif
|