Browse Source

thread_priority, frame_budget, etc.

David Rose 17 years ago
parent
commit
72dad6e9d5

+ 2 - 0
panda/src/event/asyncTask.cxx

@@ -309,6 +309,8 @@ unlock_and_do_task() {
   _total_dt += _dt;
   ++_num_frames;
 
+  _chain->_time_in_frame += _dt;
+
   return status;
 }
 

+ 0 - 22
panda/src/event/asyncTaskChain.I

@@ -25,25 +25,3 @@ INLINE bool AsyncTaskChain::
 is_started() const {
   return (_state == S_started);
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: AsyncTaskChain::set_tick_clock
-//       Access: Published
-//  Description: Sets the tick_clock flag.  When this is true,
-//               get_clock()->tick() will be called automatically at
-//               each task epoch.  This is false by default.
-////////////////////////////////////////////////////////////////////
-INLINE void AsyncTaskChain::
-set_tick_clock(bool clock) {
-  _tick_clock = clock;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: AsyncTaskChain::get_tick_clock
-//       Access: Published
-//  Description: Returns the tick_clock flag..  See set_tick_clock().
-////////////////////////////////////////////////////////////////////
-INLINE bool AsyncTaskChain::
-get_tick_clock() const {
-  return _tick_clock;
-}

+ 144 - 8
panda/src/event/asyncTaskChain.cxx

@@ -40,14 +40,18 @@ AsyncTaskChain::
 AsyncTaskChain(AsyncTaskManager *manager, const string &name) :
   Namable(name),
   _manager(manager),
+  _cvar(manager->_lock),
   _tick_clock(false),
   _num_threads(0),
-  _cvar(manager->_lock),
-  _num_tasks(0),
+  _thread_priority(TP_normal),
+  _frame_budget(-1.0),
   _num_busy_threads(0),
+  _num_tasks(0),
   _state(S_initial),
   _current_sort(INT_MAX),
-  _needs_cleanup(false)
+  _needs_cleanup(false),
+  _current_frame(0),
+  _time_in_frame(0.0)
 {
 }
 
@@ -67,6 +71,38 @@ AsyncTaskChain::
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AsyncTaskChain::set_tick_clock
+//       Access: Published
+//  Description: Sets the tick_clock flag.  When this is true,
+//               get_clock()->tick() will be called automatically at
+//               each task epoch.  This is false by default.
+////////////////////////////////////////////////////////////////////
+void AsyncTaskChain::
+set_tick_clock(bool tick_clock) {
+  if (_manager != (AsyncTaskManager *)NULL) {
+    MutexHolder holder(_manager->_lock);
+    _tick_clock = tick_clock;
+  } else {
+    _tick_clock = tick_clock;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AsyncTaskChain::get_tick_clock
+//       Access: Published
+//  Description: Returns the tick_clock flag.  See set_tick_clock().
+////////////////////////////////////////////////////////////////////
+bool AsyncTaskChain::
+get_tick_clock() const {
+  if (_manager != (AsyncTaskManager *)NULL) {
+    MutexHolder holder(_manager->_lock);
+    return _tick_clock;
+  } else {
+    return _tick_clock;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: AsyncTaskChain::set_num_threads
 //       Access: Published
@@ -120,6 +156,77 @@ get_num_running_threads() const {
   return _threads.size();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AsyncTaskChain::set_thread_priority
+//       Access: Published
+//  Description: Changes the priority associated with threads that
+//               serve this task chain.  This may require stopping the
+//               threads if they are already running.
+////////////////////////////////////////////////////////////////////
+void AsyncTaskChain::
+set_thread_priority(ThreadPriority priority) {
+  MutexHolder holder(_manager->_lock);
+  if (_thread_priority != priority) {
+    do_stop_threads();
+    _thread_priority = priority;
+
+    if (_num_tasks != 0) {
+      do_start_threads();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AsyncTaskChain::get_thread_priority
+//       Access: Published
+//  Description: Returns the priority associated with threads that
+//               serve this task chain.
+////////////////////////////////////////////////////////////////////
+ThreadPriority AsyncTaskChain::
+get_thread_priority() const {
+  MutexHolder holder(_manager->_lock);
+  return _thread_priority;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AsyncTaskChain::set_frame_budget
+//       Access: Published
+//  Description: Sets the maximum amount of time per frame the tasks
+//               on this chain are granted for execution.  If this is
+//               less than zero, there is no limit; if it is >= 0, it
+//               represents a maximum amount of time (in seconds) that
+//               will be used to execute tasks.  If this time is
+//               exceeded in any one frame, the task chain will stop
+//               executing tasks until the next frame, as defined by
+//               the TaskManager's clock.
+////////////////////////////////////////////////////////////////////
+void AsyncTaskChain::
+set_frame_budget(double frame_budget) {
+  if (_manager != (AsyncTaskManager *)NULL) {
+    MutexHolder holder(_manager->_lock);
+    _frame_budget = frame_budget;
+  } else {
+    _frame_budget = frame_budget;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AsyncTaskChain::get_frame_budget
+//       Access: Published
+//  Description: Returns the maximum amount of time per frame the
+//               tasks on this chain are granted for execution.  See
+//               set_frame_budget().
+////////////////////////////////////////////////////////////////////
+double AsyncTaskChain::
+get_frame_budget() const {
+  if (_manager != (AsyncTaskManager *)NULL) {
+    MutexHolder holder(_manager->_lock);
+    return _frame_budget;
+  } else {
+    return _frame_budget;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: AsyncTaskChain::stop_threads
 //       Access: Published
@@ -618,6 +725,7 @@ finish_sort_group() {
   // There are no more tasks in this epoch; advance to the next epoch.
   if (_tick_clock) {
     _manager->_clock->tick();
+    _manager->_frame_cvar.signal_all();
   }
   if (!_threads.empty()) {
     PStatClient::thread_tick(get_name());
@@ -705,7 +813,7 @@ do_start_threads() {
         }
         strm << "_" << i;
         PT(AsyncTaskChainThread) thread = new AsyncTaskChainThread(strm.str(), this);
-        if (thread->start(TP_low, true)) {
+        if (thread->start(_thread_priority, true)) {
           _threads.push_back(thread);
         }
       }
@@ -778,14 +886,24 @@ do_poll() {
     return;
   }
   
-  while (!_active.empty() && _state != S_shutdown && _state != S_aborting) {
+  while (!_active.empty()) {
+    if (_state == S_shutdown || _state == S_aborting) {
+      return;
+    }
+    int frame = _manager->_clock->get_frame_count();
+    if (_current_frame != frame) {
+      _current_frame = frame;
+      _time_in_frame = 0.0;
+    }
+    if (_frame_budget >= 0.0 && _time_in_frame > _frame_budget) {
+      return;
+    }
+
     _current_sort = _active.front()->get_sort();
     service_one_task(NULL);
   }
 
-  if (_state != S_shutdown && _state != S_aborting) {
-    finish_sort_group();
-  }
+  finish_sort_group();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -935,6 +1053,24 @@ thread_main() {
   while (_chain->_state != S_shutdown && _chain->_state != S_aborting) {
     if (!_chain->_active.empty() &&
         _chain->_active.front()->get_sort() == _chain->_current_sort) {
+
+      int frame = _chain->_manager->_clock->get_frame_count();
+      if (_chain->_current_frame != frame) {
+        _chain->_current_frame = frame;
+        _chain->_time_in_frame = 0.0;
+      }
+
+      // If we've exceeded our frame budget, sleep until the next
+      // frame.
+      while (_chain->_frame_budget >= 0.0 && _chain->_time_in_frame > _chain->_frame_budget) {
+        _chain->_manager->_frame_cvar.wait();
+        frame = _chain->_manager->_clock->get_frame_count();
+        if (_chain->_current_frame != frame) {
+          _chain->_current_frame = frame;
+          _chain->_time_in_frame = 0.0;
+        }
+      }
+
       PStatTimer timer(_task_pcollector);
       _chain->_num_busy_threads++;
       _chain->service_one_task(this);

+ 13 - 2
panda/src/event/asyncTaskChain.h

@@ -61,13 +61,19 @@ public:
   ~AsyncTaskChain();
 
 PUBLISHED:
-  INLINE void set_tick_clock(bool tick_clock);
-  INLINE bool get_tick_clock() const;
+  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;
+
   BLOCKING void stop_threads();
   void start_threads();
   INLINE bool is_started() const;
@@ -152,7 +158,9 @@ protected:
 
   bool _tick_clock;
   int _num_threads;
+  ThreadPriority _thread_priority;
   Threads _threads;
+  double _frame_budget;
   int _num_busy_threads;
   int _num_tasks;
   TaskHeap _active;
@@ -161,6 +169,9 @@ protected:
   State _state;
   int _current_sort;
   bool _needs_cleanup;
+
+  int _current_frame;
+  double _time_in_frame;
   
   static PStatCollector _task_pcollector;
   static PStatCollector _wait_pcollector;

+ 6 - 1
panda/src/event/asyncTaskManager.cxx

@@ -35,7 +35,8 @@ TypeHandle AsyncTaskManager::_type_handle;
 AsyncTaskManager::
 AsyncTaskManager(const string &name) :
   Namable(name),
-  _clock(ClockObject::get_global_clock())
+  _clock(ClockObject::get_global_clock()),
+  _frame_cvar(_lock)
 {
   // Make a default task chain.
   do_make_task_chain("");
@@ -468,6 +469,10 @@ poll() {
     AsyncTaskChain *chain = (*tci);
     chain->do_poll();
   }
+
+  // Just in case the clock was ticked explicitly by one of our
+  // polling chains.
+  _frame_cvar.signal_all();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 0
panda/src/event/asyncTaskManager.h

@@ -121,6 +121,8 @@ protected:
   int _num_tasks;
   TasksByName _tasks_by_name;
   PT(ClockObject) _clock;
+  
+  ConditionVarFull _frame_cvar;  // Signalled when the clock ticks.
 
 public:
   static TypeHandle get_class_type() {