Browse Source

integrate PStatThread and Thread; better thread handling in GraphicsEngine::render_frame()

David Rose 20 years ago
parent
commit
b7ceab1bff

+ 74 - 25
panda/src/display/graphicsEngine.cxx

@@ -43,6 +43,7 @@
   #include <sys/time.h>
 #endif
 
+PStatCollector GraphicsEngine::_wait_pcollector("Wait");
 PStatCollector GraphicsEngine::_app_pcollector("App");
 PStatCollector GraphicsEngine::_yield_pcollector("App:Yield");
 PStatCollector GraphicsEngine::_cull_pcollector("Cull");
@@ -490,12 +491,24 @@ render_frame() {
     }
   }
   new_windows.swap(_windows);
+
+  // Now it's time to do any drawing from the main frame--after all of
+  // the App code has executed, but before we begin the next frame.
+  _app.do_frame(this);
   
-  // Grab each thread's mutex again after all windows have flipped.
+  // Grab each thread's mutex again after all windows have flipped,
+  // and wait for the thread to finish.
   Threads::const_iterator ti;
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
     thread->_cv_mutex.lock();
+
+    if (thread->_thread_state != TS_wait) {
+      PStatTimer timer(_wait_pcollector);
+      while (thread->_thread_state != TS_wait) {
+        thread->_cv_done.wait();
+      }
+    }
   }
 
   // Now cycle the pipeline and officially begin the next frame.
@@ -524,12 +537,11 @@ render_frame() {
   }
 
   // Now signal all of our threads to begin their next frame.
-  _app.do_frame(this);
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
     if (thread->_thread_state == TS_wait) {
       thread->_thread_state = TS_do_frame;
-      thread->_cv.signal();
+      thread->_cv_start.signal();
     }
     thread->_cv_mutex.release();
   }
@@ -579,10 +591,16 @@ open_windows() {
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
     thread->_cv_mutex.lock();
-    if (thread->_thread_state == TS_wait) {
-      thread->_thread_state = TS_do_windows;
-      thread->_cv.signal();
+
+    if (thread->_thread_state != TS_wait) {
+      PStatTimer timer(_wait_pcollector);
+      while (thread->_thread_state != TS_wait) {
+        thread->_cv_done.wait();
+      }
     }
+
+    thread->_thread_state = TS_do_windows;
+    thread->_cv_start.signal();
     thread->_cv_mutex.release();
   }
 
@@ -591,10 +609,16 @@ open_windows() {
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
     thread->_cv_mutex.lock();
-    if (thread->_thread_state == TS_wait) {
-      thread->_thread_state = TS_do_windows;
-      thread->_cv.signal();
+
+    if (thread->_thread_state != TS_wait) {
+      PStatTimer timer(_wait_pcollector);
+      while (thread->_thread_state != TS_wait) {
+        thread->_cv_done.wait();
+      }
     }
+
+    thread->_thread_state = TS_do_windows;
+    thread->_cv_start.signal();
     thread->_cv_mutex.release();
   }
 }
@@ -980,21 +1004,28 @@ do_flip_frame() {
   nassertv(_flip_state == FS_draw || _flip_state == FS_sync);
 
   // First, wait for all the threads to finish their current frame, if
-  // necessary.  Grabbing the mutex should achieve that.
+  // necessary.  Grabbing the mutex (and waiting for TS_wait) should
+  // achieve that.
   Threads::const_iterator ti;
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
     thread->_cv_mutex.lock();
+
+    if (thread->_thread_state != TS_wait) {
+      PStatTimer timer(_wait_pcollector);
+      while (thread->_thread_state != TS_wait) {
+        thread->_cv_done.wait();
+      }
+    }
   }
   
   // Now signal all of our threads to flip the windows.
   _app.do_flip(this);
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
-    if (thread->_thread_state == TS_wait) {
-      thread->_thread_state = TS_do_flip;
-      thread->_cv.signal();
-    }
+    nassertv(thread->_thread_state == TS_wait);
+    thread->_thread_state = TS_do_flip;
+    thread->_cv_start.signal();
     thread->_cv_mutex.release();
   }
 
@@ -1305,10 +1336,15 @@ terminate_threads() {
   // Now tell them to release their windows' graphics contexts.
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
-    if (thread->_thread_state == TS_wait) {
-      thread->_thread_state = TS_do_release;
-      thread->_cv.signal();
+
+    if (thread->_thread_state != TS_wait) {
+      PStatTimer timer(_wait_pcollector);
+      while (thread->_thread_state != TS_wait) {
+        thread->_cv_done.wait();
+      }
     }
+    thread->_thread_state = TS_do_release;
+    thread->_cv_start.signal();
     thread->_cv_mutex.release();
   }
 
@@ -1322,7 +1358,7 @@ terminate_threads() {
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
     thread->_thread_state = TS_terminate;
-    thread->_cv.signal();
+    thread->_cv_start.signal();
     thread->_cv_mutex.release();
   }
 
@@ -1738,9 +1774,10 @@ do_callbacks(GraphicsEngine::CallbackTime callback_time) {
 ////////////////////////////////////////////////////////////////////
 GraphicsEngine::RenderThread::
 RenderThread(const string &name, GraphicsEngine *engine) : 
-  Thread(name),
+  Thread(name, "Main"),
   _engine(engine),
-  _cv(_cv_mutex)
+  _cv_start(_cv_mutex),
+  _cv_done(_cv_mutex)
 {
   _thread_state = TS_wait;
 }
@@ -1756,6 +1793,8 @@ void GraphicsEngine::RenderThread::
 thread_main() {
   MutexHolder holder(_cv_mutex);
   while (true) {
+    nassertv(_cv_mutex.debug_is_locked());
+
     switch (_thread_state) {
     case TS_wait:
       break;
@@ -1763,31 +1802,41 @@ thread_main() {
     case TS_do_frame:
       do_pending(_engine);
       do_frame(_engine);
-      _thread_state = TS_wait;
       break;
 
     case TS_do_flip:
       do_flip(_engine);
-      _thread_state = TS_wait;
       break;
 
     case TS_do_release:
       do_pending(_engine);
       do_release(_engine);
-      _thread_state = TS_wait;
       break;
 
     case TS_do_windows:
       do_windows(_engine);
       do_pending(_engine);
-      _thread_state = TS_wait;
       break;
 
     case TS_terminate:
       do_pending(_engine);
       do_close(_engine);
+      _thread_state = TS_done;
+      _cv_done.signal();
+      return;
+
+    case TS_done:
+      // Shouldn't be possible to get here.
+      nassertv(false);
       return;
     }
-    _cv.wait();
+
+    _thread_state = TS_wait;
+    _cv_done.signal();
+
+    {
+      PStatTimer timer(_wait_pcollector);
+      _cv_start.wait();
+    }
   }
 }

+ 5 - 2
panda/src/display/graphicsEngine.h

@@ -107,7 +107,8 @@ public:
     TS_do_flip,
     TS_do_release,
     TS_do_windows,
-    TS_terminate
+    TS_terminate,
+    TS_done
   };
 
   enum CallbackTime {
@@ -216,7 +217,8 @@ private:
 
     GraphicsEngine *_engine;
     Mutex _cv_mutex;
-    ConditionVar _cv;
+    ConditionVar _cv_start;
+    ConditionVar _cv_done;
     ThreadState _thread_state;
   };
 
@@ -243,6 +245,7 @@ private:
   FlipState _flip_state;
   Mutex _lock;
 
+  static PStatCollector _wait_pcollector;
   static PStatCollector _app_pcollector;
   static PStatCollector _yield_pcollector;
   static PStatCollector _cull_pcollector;

+ 3 - 0
panda/src/express/Sources.pp

@@ -25,6 +25,7 @@
     dcast.T dcast.h \
     encryptStreamBuf.h encryptStreamBuf.I encryptStream.h encryptStream.I \
     error_utils.h \
+    externalThread.h \
     hashGeneratorBase.I hashGeneratorBase.h \
     hashVal.I hashVal.h \
     indirectLess.I indirectLess.h \
@@ -88,6 +89,7 @@
     datagramSink.cxx dcast.cxx \
     encryptStreamBuf.cxx encryptStream.cxx \
     error_utils.cxx \
+    externalThread.cxx \
     hashGeneratorBase.cxx hashVal.cxx \
     mainThread.cxx \
     memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
@@ -145,6 +147,7 @@
     datagramSink.I datagramSink.h dcast.T dcast.h \
     encryptStreamBuf.h encryptStreamBuf.I encryptStream.h encryptStream.I \
     error_utils.h  \
+    externalThread.h \
     hashGeneratorBase.I \
     hashGeneratorBase.h hashVal.I hashVal.h \
     indirectLess.I indirectLess.h \

+ 1 - 0
panda/src/express/express_composite1.cxx

@@ -16,6 +16,7 @@
 #include "error_utils.cxx"
 #include "encryptStreamBuf.cxx"
 #include "encryptStream.cxx"
+#include "externalThread.cxx"
 #include "hashGeneratorBase.cxx"
 #include "hashVal.cxx"
 #include "mainThread.cxx"

+ 40 - 0
panda/src/express/externalThread.cxx

@@ -0,0 +1,40 @@
+// Filename: externalThread.cxx
+// Created by:  drose (30Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "externalThread.h"
+
+TypeHandle ExternalThread::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ExternalThread::Constructor
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ExternalThread::
+ExternalThread() : Thread("External", "External") {
+  _started = true;
+}
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: ExternalThread::thread_main
+//       Access: Private, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ExternalThread::
+thread_main() {
+}

+ 56 - 0
panda/src/express/externalThread.h

@@ -0,0 +1,56 @@
+// Filename: externalThread.h
+// Created by:  drose (30Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EXTERNALTHREAD_H
+#define EXTERNALTHREAD_H
+
+#include "pandabase.h"
+#include "thread.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ExternalThread
+// Description : The special "external thread" class.  There is one
+//               instance of these in the world, and it is returned by
+//               Thread::get_external_thread().
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS ExternalThread : public Thread {
+private:
+  ExternalThread();
+  virtual void thread_main();
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    Thread::init_type();
+    register_type(_type_handle, "ExternalThread",
+                  Thread::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 Thread;
+};
+
+#endif

+ 2 - 1
panda/src/express/mainThread.cxx

@@ -26,7 +26,8 @@ TypeHandle MainThread::_type_handle;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 MainThread::
-MainThread() : Thread("main") {
+MainThread() : Thread("Main", "Main") {
+  _started = true;
 }
  
 ////////////////////////////////////////////////////////////////////

+ 89 - 6
panda/src/express/thread.I

@@ -27,10 +27,22 @@
 //               terminate and destruct itself.
 //
 //               Call start() to begin thread execution.
+//
+//               The name should be unique for each thread (though
+//               this is not enforced, and not strictly required).
+//               The sync_name can be shared between multiple
+//               different threads; threads that run synchronously
+//               with each other should be given the same sync_name,
+//               for the benefit of PStats.
 ////////////////////////////////////////////////////////////////////
 INLINE Thread::
-Thread(const string &name) : _name(name), _impl(this) {
+Thread(const string &name, const string &sync_name) : 
+  _name(name), 
+  _sync_name(sync_name), 
+  _impl(this) 
+{
   _started = false;
+  _pstats_index = -1;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -56,13 +68,55 @@ operator = (const Thread &copy) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_name
 //       Access: Public
-//  Description: Returns the name of the thread.
+//  Description: Returns the name of the thread.  Each thread should
+//               probably have a unique name, but this is not enforced
+//               (and not strictly required).
 ////////////////////////////////////////////////////////////////////
 INLINE const string &Thread::
 get_name() const {
   return _name;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::get_sync_name
+//       Access: Public
+//  Description: Returns the sync name of the thread.  This name
+//               collects threads into "sync groups", which are
+//               expected to run synchronously.  This is mainly used
+//               for the benefit of PStats; threads with the same sync
+//               name can be ticked all at once via the thread_tick()
+//               call.
+////////////////////////////////////////////////////////////////////
+INLINE const string &Thread::
+get_sync_name() const {
+  return _sync_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::get_pstats_index
+//       Access: Public
+//  Description: Returns the PStats index associated with this thread,
+//               or -1 if no index has yet been associated with this
+//               thread.  This is used internally by the PStatClient;
+//               you should not need to call this directly.
+////////////////////////////////////////////////////////////////////
+INLINE int Thread::
+get_pstats_index() const {
+  return _pstats_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::set_pstats_index
+//       Access: Public
+//  Description: Stores a PStats index to be associated with this
+//               thread.  This is used internally by the PStatClient;
+//               you should not need to call this directly.
+////////////////////////////////////////////////////////////////////
+INLINE void Thread::
+set_pstats_index(int pstats_index) {
+  _pstats_index = pstats_index;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::start
 //       Access: Public
@@ -147,18 +201,47 @@ get_main_thread() {
   return _main_thread;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::get_external_thread
+//       Access: Public, Static
+//  Description: Returns a pointer to the "external" Thread
+//               object--this is a special Thread object that
+//               corresponds to any thread spawned outside of Panda's
+//               threading interface.  Note that multiple different
+//               threads may share this same pointer.
+////////////////////////////////////////////////////////////////////
+INLINE Thread *Thread::
+get_external_thread() {
+  if (_external_thread == (Thread *)NULL) {
+    init_external_thread();
+  }
+  return _external_thread;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_current_thread
 //       Access: Public, Static
 //  Description: Returns a pointer to the currently-executing Thread
 //               object.  If this is called from the main thread, this
-//               will return the same value as get_main_thread().  If
-//               the current thread was spawned outside of Panda's
-//               threading system, this might return NULL.
+//               will return the same value as get_main_thread().
+//
+//               This will always return some valid Thread pointer.
+//               It will never return NULL, even if the current thread
+//               was spawned outside of Panda's threading system,
+//               although all non-Panda threads will return the exact
+//               same Thread pointer.
 ////////////////////////////////////////////////////////////////////
 INLINE Thread *Thread::
 get_current_thread() {
-  return ThreadImpl::get_current_thread();
+#ifndef HAVE_THREADS
+  return get_main_thread();
+#else  // HAVE_THREADS
+  Thread *thread = ThreadImpl::get_current_thread();
+  if (thread == (Thread *)NULL) {
+    return Thread::get_external_thread();
+  }
+  return thread;
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////

+ 15 - 1
panda/src/express/thread.cxx

@@ -18,8 +18,10 @@
 
 #include "thread.h"
 #include "mainThread.h"
+#include "externalThread.h"
 
 Thread *Thread::_main_thread;
+Thread *Thread::_external_thread;
 TypeHandle Thread::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -51,6 +53,18 @@ void Thread::
 init_main_thread() {
   if (_main_thread == (Thread *)NULL) {
     _main_thread = new MainThread;
-    ((Thread *)_main_thread)->_started = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::init_external_thread
+//       Access: Private, Static
+//  Description: Creates the Thread object that represents all of the
+//               external threads.
+////////////////////////////////////////////////////////////////////
+void Thread::
+init_external_thread() {
+  if (_external_thread == (Thread *)NULL) {
+    _external_thread = new ExternalThread;
   }
 }

+ 12 - 1
panda/src/express/thread.h

@@ -39,7 +39,7 @@
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDAEXPRESS Thread : public TypedReferenceCount {
 public:
-  INLINE Thread(const string &name);
+  INLINE Thread(const string &name, const string &sync_name);
   virtual ~Thread();
 
 private:
@@ -51,6 +51,10 @@ protected:
 
 public:
   INLINE const string &get_name() const;
+  INLINE const string &get_sync_name() const;
+
+  INLINE int get_pstats_index() const;
+  INLINE void set_pstats_index(int pstats_index);
 
   INLINE bool start(ThreadPriority priority, bool global, bool joinable);
   INLINE void interrupt();
@@ -59,6 +63,7 @@ public:
   INLINE static void prepare_for_exit();
 
   INLINE static Thread *get_main_thread();
+  INLINE static Thread *get_external_thread();
   INLINE static Thread *get_current_thread();
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
@@ -67,12 +72,18 @@ public:
 
 private:
   static void init_main_thread();
+  static void init_external_thread();
 
+protected:
   bool _started;
   string _name;
+  string _sync_name;
   ThreadImpl _impl;
+  int _pstats_index;
 
+private:
   static Thread *_main_thread;
+  static Thread *_external_thread;
 
 public:
   static TypeHandle get_class_type() {

+ 24 - 0
panda/src/express/weakPointerTo.I

@@ -106,6 +106,18 @@ p() const {
   return (To *)WeakPointerToBase<T>::_void_ptr;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WeakPointerTo::get_orig
+//       Access: Public
+//  Description: Returns the original pointer value, even if the
+//               object has since been deleted.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE TYPENAME WeakPointerTo<T>::To *WeakPointerTo<T>::
+get_orig() const {
+  return (To *)WeakPointerToBase<T>::_void_ptr;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WeakPointerTo::Assignment operator
 //       Access: Public
@@ -258,6 +270,18 @@ p() const {
   return (To *)WeakPointerToBase<T>::_void_ptr;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: WeakConstPointerTo::get_orig
+//       Access: Public
+//  Description: Returns the original pointer value, even if the
+//               object has since been deleted.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE const TYPENAME WeakConstPointerTo<T>::To *WeakConstPointerTo<T>::
+get_orig() const {
+  return (To *)WeakPointerToBase<T>::_void_ptr;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WeakConstPointerTo::Assignment operator
 //       Access: Public

+ 2 - 0
panda/src/express/weakPointerTo.h

@@ -47,6 +47,7 @@ public:
 
 PUBLISHED:
   INLINE To *p() const;
+  INLINE To *get_orig() const;
 
   INLINE WeakPointerTo<T> &operator = (To *ptr);
   INLINE WeakPointerTo<T> &operator = (const PointerTo<T> &copy);
@@ -84,6 +85,7 @@ public:
 
 PUBLISHED:
   INLINE const To *p() const;
+  INLINE const To *get_orig() const;
 
   INLINE WeakConstPointerTo<T> &operator = (const To *ptr);
   INLINE WeakConstPointerTo<T> &operator = (const PointerTo<T> &copy);

+ 2 - 1
panda/src/pstatclient/Sources.pp

@@ -24,7 +24,8 @@
      pStatClientVersion.cxx  \
      pStatClientControlMessage.cxx pStatCollectorDef.cxx  \
      pStatFrameData.cxx pStatProperties.cxx  \
-     pStatServerControlMessage.cxx 
+     pStatServerControlMessage.cxx \
+     pStatThread.cxx
 
   #define INSTALL_HEADERS \
     config_pstats.h pStatClient.I pStatClient.h \

+ 28 - 0
panda/src/pstatclient/pStatClient.I

@@ -120,6 +120,34 @@ get_thread_name(int index) const {
   return _threads[index]._name;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_thread_sync_name
+//       Access: Published
+//  Description: Returns the sync_name of the indicated thread.
+////////////////////////////////////////////////////////////////////
+INLINE string PStatClient::
+get_thread_sync_name(int index) const {
+  ReMutexHolder holder(_lock);
+  nassertr(index >= 0 && index < (int)_threads.size(), string());
+  return _threads[index]._sync_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_thread_object
+//       Access: Published
+//  Description: Returns the Panda Thread object associated with the
+//               indicated PStatThread.
+////////////////////////////////////////////////////////////////////
+INLINE Thread *PStatClient::
+get_thread_object(int index) const {
+  ReMutexHolder holder(_lock);
+  nassertr(index >= 0 && index < (int)_threads.size(), NULL);
+  if (_threads[index]._thread.was_deleted()) {
+    return NULL;
+  }
+  return _threads[index]._thread;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_clock
 //       Access: Published

+ 128 - 36
panda/src/pstatclient/pStatClient.cxx

@@ -29,6 +29,7 @@
 #include "pStatThread.h"
 #include "config_pstats.h"
 #include "pStatProperties.h"
+#include "thread.h"
 
 PStatCollector PStatClient::_total_size_pcollector("Memory usage");
 PStatCollector PStatClient::_cpp_size_pcollector("Memory usage:C++");
@@ -68,8 +69,8 @@ PStatClient() :
   //collector._def->_suggested_color.set(0.5, 0.5, 0.5);
   _collectors.push_back(collector);
 
-  // We also always have a thread at index 0 named "Main".
-  make_thread("Main");
+  // The main thread is always at index 0.
+  make_thread(Thread::get_main_thread());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -148,20 +149,40 @@ get_thread(int index) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_main_thread
 //       Access: Published
-//  Description: Returns a handle to the client's "Main", or default,
-//               thread.  This is where collectors will be started and
-//               stopped if they don't specify otherwise.
+//  Description: Returns a handle to the client's Main thread.  This
+//               is the thread that started the application.
 ////////////////////////////////////////////////////////////////////
 PStatThread PStatClient::
 get_main_thread() const {
   return PStatThread((PStatClient *)this, 0);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_current_thread
+//       Access: Published
+//  Description: Returns a handle to the currently-executing thread.
+//               This is the thread that PStatCollectors will be
+//               counted in if they do not specify otherwise.
+////////////////////////////////////////////////////////////////////
+PStatThread PStatClient::
+get_current_thread() const {
+  Thread *thread = Thread::get_current_thread();
+  int thread_index = thread->get_pstats_index();
+  if (thread_index != -1) {
+    return PStatThread((PStatClient *)this, thread_index);
+  }
+
+  // This is the first time we have encountered this current Thread.
+  // Make a new PStatThread object for it.
+  return ((PStatClient *)this)->make_thread(thread);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::main_tick
 //       Access: Published, Static
 //  Description: A convenience function to call new_frame() on the
-//               global PStatClient's main thread.
+//               global PStatClient's main thread, and any other
+//               threads with a sync_name of "Main".
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 main_tick() {
@@ -185,19 +206,65 @@ main_tick() {
   get_global_pstats()->client_main_tick();
 }  
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::thread_tick
+//       Access: Published, Static
+//  Description: A convenience function to call new_frame() on any
+//               threads with the indicated sync_name
+////////////////////////////////////////////////////////////////////
+void PStatClient::
+thread_tick(const string &sync_name) {
+  get_global_pstats()->client_thread_tick(sync_name);
+}  
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::client_main_tick
 //       Access: Published
 //  Description: A convenience function to call new_frame() on the
-//               the given client's main thread.
+//               given PStatClient's main thread, and any other
+//               threads with a sync_name of "Main".
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 client_main_tick() {
   ReMutexHolder holder(_lock);
   if (has_impl()) {
     _impl->client_main_tick();
+
+    MultiThingsByName::const_iterator ni =
+      _threads_by_sync_name.find("Main");
+    if (ni != _threads_by_sync_name.end()) {
+      const vector_int &indices = (*ni).second;
+      for (vector_int::const_iterator vi = indices.begin(); 
+           vi != indices.end(); 
+           ++vi) {
+        _impl->new_frame(*vi);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::client_thread_tick
+//       Access: Published
+//  Description: A convenience function to call new_frame() on all of
+//               the threads with the indicated sync name.
+////////////////////////////////////////////////////////////////////
+void PStatClient::
+client_thread_tick(const string &sync_name) {
+  ReMutexHolder holder(_lock);
+
+  if (has_impl()) {
+    MultiThingsByName::const_iterator ni =
+      _threads_by_sync_name.find(sync_name);
+    if (ni != _threads_by_sync_name.end()) {
+      const vector_int &indices = (*ni).second;
+      for (vector_int::const_iterator vi = indices.begin(); 
+           vi != indices.end(); 
+           ++vi) {
+        _impl->new_frame(*vi);
+      }
+    }
   }
-  get_main_thread().new_frame();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -347,35 +414,56 @@ make_collector_with_name(int parent_index, const string &name) {
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::make_thread
 //       Access: Private
-//  Description: Returns a PStatThread with the indicated name
-//               suitable for grouping collectors.  This is normally
-//               called by a PStatThread constructor.
+//  Description: Returns a PStatThread for the indicated Panda Thread
+//               object.  This is normally called by a PStatThread
+//               constructor.
 ////////////////////////////////////////////////////////////////////
 PStatThread PStatClient::
-make_thread(const string &name) {
+make_thread(Thread *thread) {
   ReMutexHolder holder(_lock);
 
-  ThingsByName::const_iterator ni =
-    _threads_by_name.find(name);
+  int thread_index = thread->get_pstats_index();
+  if (thread_index != -1) {
+    return PStatThread((PStatClient *)this, thread_index);
+  }
+
+  MultiThingsByName::const_iterator ni =
+    _threads_by_name.find(thread->get_name());
 
   if (ni != _threads_by_name.end()) {
-    // We already had a thread by this name; return it.
-    int index = (*ni).second;
-    nassertr(index >= 0 && index < (int)_threads.size(), PStatThread());
-    return PStatThread(this, (*ni).second);
+    // We have seen a thread with this name before.  Can we re-use any
+    // of them?
+    const vector_int &indices = (*ni).second;
+    for (vector_int::const_iterator vi = indices.begin(); 
+         vi != indices.end(); 
+         ++vi) {
+      int index = (*vi);
+      nassertr(index >= 0 && index < (int)_threads.size(), PStatThread());
+      if (_threads[index]._thread.was_deleted() &&
+          _threads[index]._sync_name == thread->get_sync_name()) {
+        // Yes, re-use this one.
+        _threads[index]._thread = thread;
+        thread->set_pstats_index(index);
+        return PStatThread(this, index);
+      }
+    }
   }
 
-  // Create a new thread for this name.
+  // Create a new PStatsThread for this thread pointer.
   int new_index = _threads.size();
-  _threads_by_name.insert(ThingsByName::value_type(name, new_index));
-
-  Thread thread;
-  thread._name = name;
-  thread._is_active = false;
-  thread._next_packet = 0.0;
-  thread._frame_number = 0;
-
-  _threads.push_back(thread);
+  thread->set_pstats_index(new_index);
+  _threads_by_name[thread->get_name()].push_back(new_index);
+  _threads_by_sync_name[thread->get_sync_name()].push_back(new_index);
+        
+  InternalThread pthread;
+  pthread._thread = thread;
+  pthread._name = thread->get_name();
+  pthread._sync_name = thread->get_sync_name();
+  pthread._is_active = false;
+  pthread._next_packet = 0.0;
+  pthread._frame_number = 0;
+
+  _threads.push_back(pthread);
 
   // We need an additional PerThreadData for this thread in all of the
   // collectors.
@@ -508,10 +596,12 @@ stop(int collector_index, int thread_index) {
       _collectors[collector_index].is_active() &&
       _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
-      pstats_cat.warning()
-        << "Collector " << get_collector_fullname(collector_index)
-        << " was already stopped in thread " << get_thread_name(thread_index)
-        << "!\n";
+      if (pstats_cat.is_debug()) {
+        pstats_cat.debug()
+          << "Collector " << get_collector_fullname(collector_index)
+          << " was already stopped in thread " << get_thread_name(thread_index)
+          << "!\n";
+      }
       return;
     }
 
@@ -546,10 +636,12 @@ stop(int collector_index, int thread_index, float as_of) {
       _collectors[collector_index].is_active() &&
       _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
-      pstats_cat.warning()
-        << "Collector " << get_collector_fullname(collector_index)
-        << " was already stopped in thread " << get_thread_name(thread_index)
-        << "!\n";
+      if (pstats_cat.is_debug()) {
+        pstats_cat.debug()
+          << "Collector " << get_collector_fullname(collector_index)
+          << " was already stopped in thread " << get_thread_name(thread_index)
+          << "!\n";
+      }
       return;
     }
 

+ 15 - 5
panda/src/pstatclient/pStatClient.h

@@ -28,6 +28,9 @@
 #include "reMutexHolder.h"
 #include "luse.h"
 #include "pmap.h"
+#include "thread.h"
+#include "weakPointerTo.h"
+#include "vector_int.h"
 
 class PStatCollector;
 class PStatCollectorDef;
@@ -71,8 +74,11 @@ PUBLISHED:
   INLINE int get_num_threads() const;
   PStatThread get_thread(int index) const;
   INLINE string get_thread_name(int index) const;
+  INLINE string get_thread_sync_name(int index) const;
+  INLINE Thread *get_thread_object(int index) const;
 
   PStatThread get_main_thread() const;
+  PStatThread get_current_thread() const;
 
   INLINE const ClockObject &get_clock() const;
 
@@ -83,8 +89,10 @@ PUBLISHED:
   INLINE static void resume_after_pause();
 
   static void main_tick();
+  static void thread_tick(const string &sync_name);
 
   void client_main_tick();
+  void client_thread_tick(const string &sync_name);
   INLINE bool client_connect(string hostname, int port);
   void client_disconnect();
   INLINE bool client_is_connected() const;
@@ -100,7 +108,7 @@ private:
 
   PStatCollector make_collector_with_relname(int parent_index, string relname);
   PStatCollector make_collector_with_name(int parent_index, const string &name);
-  PStatThread make_thread(const string &name);
+  PStatThread make_thread(Thread *thread);
 
   bool is_active(int collector_index, int thread_index) const;
   bool is_started(int collector_index, int thread_index) const;
@@ -119,9 +127,9 @@ private:
   // This mutex protects everything in this class.
   ReMutex _lock;
 
-  // Not a phash_map, so the threads remain sorted by name.
   typedef pmap<string, int> ThingsByName;
-  ThingsByName _threads_by_name;
+  typedef pmap<string, vector_int> MultiThingsByName;
+  MultiThingsByName _threads_by_name, _threads_by_sync_name;
 
   // This is for the data that is per-collector, per-thread.  A vector
   // of these is stored in each Collector object, below, indexed by
@@ -169,15 +177,17 @@ private:
   // This defines a single thread, i.e. a separate chain of execution,
   // independent of all other threads.  Timing and level data are
   // maintained separately for each thread.
-  class Thread {
+  class InternalThread {
   public:
+    WPT(Thread) _thread;
     string _name;
+    string _sync_name;
     PStatFrameData _frame_data;
     bool _is_active;
     int _frame_number;
     float _next_packet;
   };
-  typedef pvector<Thread> Threads;
+  typedef pvector<InternalThread> Threads;
   Threads _threads;
 
   PStatClientImpl *_impl;

+ 8 - 8
panda/src/pstatclient/pStatClientImpl.cxx

@@ -168,7 +168,7 @@ void PStatClientImpl::
 new_frame(int thread_index) {
   nassertv(thread_index >= 0 && thread_index < (int)_client->_threads.size());
 
-  PStatClient::Thread &thread = _client->_threads[thread_index];
+  PStatClient::InternalThread &pthread = _client->_threads[thread_index];
 
   // If we're the main thread, we should exchange control packets with
   // the server.
@@ -179,34 +179,34 @@ new_frame(int thread_index) {
   // If we've got the UDP port by the time the frame starts, it's
   // time to become active and start actually tracking data.
   if (_got_udp_port) {
-    thread._is_active = true;
+    pthread._is_active = true;
   }
 
-  if (!thread._is_active) {
+  if (!pthread._is_active) {
     return;
   }
 
   float frame_start = _clock.get_real_time();
 
-  if (!thread._frame_data.is_empty()) {
+  if (!pthread._frame_data.is_empty()) {
     // Collector 0 is the whole frame.
     _client->stop(0, thread_index, frame_start);
 
     // Fill up the level data for all the collectors who have level
-    // data for this thread.
+    // data for this pthread.
     int num_collectors = _client->_collectors.size();
     for (int i = 0; i < num_collectors; i++) {
       const PStatClient::PerThreadData &ptd = 
         _client->_collectors[i]._per_thread[thread_index];
       if (ptd._has_level) {
-        thread._frame_data.add_level(i, ptd._level);
+        pthread._frame_data.add_level(i, ptd._level);
       }
     }
     transmit_frame_data(thread_index);
   }
 
-  thread._frame_data.clear();
-  thread._frame_number++;
+  pthread._frame_data.clear();
+  pthread._frame_number++;
   _client->start(0, thread_index, frame_start);
 
   // Also record the time for the PStats operation itself.

+ 49 - 13
panda/src/pstatclient/pStatCollector.I

@@ -134,7 +134,11 @@ operator = (const PStatCollector &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool PStatCollector::
 is_active() {
+#ifndef HAVE_THREADS
   return _client->is_active(_index, 0);
+#else  // HAVE_THREADS
+  return is_active(_client->get_current_thread());
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -145,7 +149,11 @@ is_active() {
 ////////////////////////////////////////////////////////////////////
 INLINE bool PStatCollector::
 is_started() {
+#ifndef HAVE_THREADS
   return _client->is_started(_index, 0);
+#else  // HAVE_THREADS
+  return is_started(_client->get_current_thread());
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -156,7 +164,11 @@ is_started() {
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
 start() {
+#ifndef HAVE_THREADS
   _client->start(_index, 0);
+#else  // HAVE_THREADS
+  start(_client->get_current_thread());
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -167,7 +179,11 @@ start() {
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
 stop() {
+#ifndef HAVE_THREADS
   _client->stop(_index, 0);
+#else  // HAVE_THREADS
+  stop(_client->get_current_thread());
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -180,7 +196,11 @@ stop() {
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
 clear_level() {
+#ifndef HAVE_THREADS
   _client->clear_level(_index, 0);
+#else  // HAVE_THREADS
+  clear_level(_client->get_current_thread());
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -192,7 +212,11 @@ clear_level() {
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
 set_level(float level) {
+#ifndef HAVE_THREADS
   _client->set_level(_index, 0, level);
+#else  // HAVE_THREADS
+  set_level(_client->get_current_thread(), level);
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -206,7 +230,11 @@ set_level(float level) {
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
 add_level(float increment) {
+#ifndef HAVE_THREADS
   _client->add_level(_index, 0, increment);
+#else  // HAVE_THREADS
+  add_level(_client->get_current_thread(), increment);
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -220,7 +248,11 @@ add_level(float increment) {
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
 sub_level(float decrement) {
-  _client->add_level(_index, 0, -decrement);
+#ifndef HAVE_THREADS
+  _client->sub_level(_index, 0, decrement);
+#else  // HAVE_THREADS
+  sub_level(_client->get_current_thread(), decrement);
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -230,12 +262,16 @@ sub_level(float decrement) {
 ////////////////////////////////////////////////////////////////////
 INLINE float PStatCollector::
 get_level() {
+#ifndef HAVE_THREADS
   return _client->get_level(_index, 0);
+#else  // HAVE_THREADS
+  return get_level(_client->get_current_thread());
+#endif  // HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::is_active
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if this particular collector is active
 //               on the indicated thread, and we are currently
 //               transmitting PStats data.
@@ -247,7 +283,7 @@ is_active(const PStatThread &thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::is_started
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if this particular collector has been
 //               started on the indicated thread, or false otherwise.
 ////////////////////////////////////////////////////////////////////
@@ -258,7 +294,7 @@ is_started(const PStatThread &thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::start
-//       Access: Public
+//       Access: Published
 //  Description: Starts this timer ticking within a particular thread.
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
@@ -268,7 +304,7 @@ start(const PStatThread &thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::start
-//       Access: Public
+//       Access: Published
 //  Description: Marks that the timer should have been started as of
 //               the indicated time.  This must be a time based on the
 //               PStatClient's clock (see PStatClient::get_clock()),
@@ -282,7 +318,7 @@ start(const PStatThread &thread, float as_of) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::stop
-//       Access: Public
+//       Access: Published
 //  Description: Stops this timer within a particular thread.
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatCollector::
@@ -292,7 +328,7 @@ stop(const PStatThread &thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::stop
-//       Access: Public
+//       Access: Published
 //  Description: Marks that the timer should have been stopped as of
 //               the indicated time.  This must be a time based on the
 //               PStatClient's clock (see PStatClient::get_clock()),
@@ -306,7 +342,7 @@ stop(const PStatThread &thread, float as_of) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::clear_level
-//       Access: Public
+//       Access: Published
 //  Description: Removes the level setting associated with this
 //               collector for the indicated thread.  The collector
 //               will no longer show up on any level graphs in this
@@ -319,7 +355,7 @@ clear_level(const PStatThread &thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::set_level
-//       Access: Public
+//       Access: Published
 //  Description: Sets the level setting associated with this
 //               collector for the indicated thread to the indicated
 //               value.
@@ -331,7 +367,7 @@ set_level(const PStatThread &thread, float level) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::add_level
-//       Access: Public
+//       Access: Published
 //  Description: Adds the indicated increment (which may be negative)
 //               to the level setting associated with this collector
 //               for the indicated thread.  If the collector did not
@@ -345,7 +381,7 @@ add_level(const PStatThread &thread, float increment) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::sub_level
-//       Access: Public
+//       Access: Published
 //  Description: Subtracts the indicated decrement (which may be
 //               negative) to the level setting associated with this
 //               collector for the indicated thread.  If the collector
@@ -359,7 +395,7 @@ sub_level(const PStatThread &thread, float decrement) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::get_level
-//       Access: Public
+//       Access: Published
 //  Description: Returns the current level value of the given collector.
 ////////////////////////////////////////////////////////////////////
 INLINE float PStatCollector::
@@ -369,7 +405,7 @@ get_level(const PStatThread &thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::get_index
-//       Access: Public
+//       Access: Published
 //  Description: Returns the index number of this particular collector
 //               within the PStatClient.
 ////////////////////////////////////////////////////////////////////

+ 4 - 4
panda/src/pstatclient/pStatCollector.h

@@ -77,9 +77,6 @@ PUBLISHED:
   INLINE void sub_level(float decrement);
   INLINE float get_level();
 
-  INLINE int get_index() const;
-
-public:
   INLINE bool is_active(const PStatThread &thread);
   INLINE bool is_started(const PStatThread &thread);
   INLINE void start(const PStatThread &thread);
@@ -93,6 +90,8 @@ public:
   INLINE void sub_level(const PStatThread &thread, float decrement);
   INLINE float get_level(const PStatThread &thread);
 
+  INLINE int get_index() const;
+
 private:
   PStatClient *_client;
   int _index;
@@ -117,7 +116,6 @@ PUBLISHED:
   INLINE void sub_level(float) { }
   INLINE float get_level() { return 0.0; }
 
-public:
   INLINE bool is_active(const PStatThread &) { return false; }
   INLINE void start(const PStatThread &) { }
   INLINE void start(const PStatThread &, float) { }
@@ -130,6 +128,8 @@ public:
   INLINE void sub_level(const PStatThread &, float) { }
   INLINE float get_level(const PStatThread &) { return 0.0; }
 
+  INLINE int get_index() const { return 0; }
+
 #endif  // DO_PSTATS
 };
 

+ 1 - 0
panda/src/pstatclient/pStatProperties.cxx

@@ -109,6 +109,7 @@ struct LevelCollectorProperties {
 };
 
 static TimeCollectorProperties time_properties[] = {
+  { 1, "Wait",                             { 0.6, 0.6, 0.6 } },
   { 1, "App",                              { 0.0, 0.8, 0.4 },  1.0 / 30.0 },
   { 1, "App:Collisions",                   { 1.0, 0.5, 0.0 } },
   { 1, "App:Collisions:Reset",             { 0.0, 0.0, 0.5 } },

+ 18 - 5
panda/src/pstatclient/pStatThread.I

@@ -48,17 +48,15 @@ PStatThread(PStatClient *client, int index) :
 //  Description: Creates a new named thread.  This will be used to
 //               unify tasks that share a common thread, and
 //               differentiate tasks that occur in different threads.
-//               If any two different PStatThread objects share the
-//               same name, then they are really the same thread.
 ////////////////////////////////////////////////////////////////////
 INLINE PStatThread::
-PStatThread(const string &name, PStatClient *client) {
+PStatThread(Thread *thread, PStatClient *client) {
 #ifdef DO_PSTATS
   if (client == (PStatClient *)NULL) {
     client = PStatClient::get_global_pstats();
   }
-  PStatThread thread(client->make_thread(name));
-  (*this) = thread;
+  PStatThread pthread(client->make_thread(thread));
+  (*this) = pthread;
 #else
   _client = (PStatClient *)NULL;
   _index = 0;
@@ -95,6 +93,10 @@ operator = (const PStatThread &copy) {
 //               whatever a frame may be deemed to be, to accumulate
 //               all the stats that have collected so far for the
 //               thread and ship them off to the server.
+//
+//               Calling PStatClient::thread_tick() will automatically
+//               call this for any threads with the indicated sync
+//               name.
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatThread::
 new_frame() {
@@ -102,3 +104,14 @@ new_frame() {
   _client->get_impl()->new_frame(_index);
 #endif
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatThread::get_index
+//       Access: Published
+//  Description: Returns the index number of this particular thread
+//               within the PStatClient.
+////////////////////////////////////////////////////////////////////
+INLINE int PStatThread::
+get_index() const {
+  return _index;
+}

+ 31 - 0
panda/src/pstatclient/pStatThread.cxx

@@ -0,0 +1,31 @@
+// Filename: pStatThread.cxx
+// Created by:  drose (30Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pStatThread.h"
+#include "pStatClient.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatThread::get_thread
+//       Access: Published
+//  Description: Returns the Panda Thread object associated with this
+//               particular PStatThread.
+////////////////////////////////////////////////////////////////////
+INLINE Thread *PStatThread::
+get_thread() const {
+  return _client->get_thread_object(_index);
+}

+ 9 - 8
panda/src/pstatclient/pStatThread.h

@@ -23,29 +23,30 @@
 
 #include "pStatClient.h"
 
+class Thread;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : PStatThread
 // Description : A lightweight class that represents a single thread
-//               of execution to PStats.  It doesn't have any real
-//               connection to any actual threads, but it's used to
-//               differentiate tasks which run in different threads,
-//               so we can measure them properly.  It is presently the
-//               user's responsibility to correctly differentiate the
-//               various threads.
+//               of execution to PStats.  It corresponds one-to-one
+//               with Panda's Thread instance.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA PStatThread {
 private:
   INLINE PStatThread();
   INLINE PStatThread(PStatClient *client, int index);
 
-public:
-  INLINE PStatThread(const string &name, PStatClient *client = NULL);
+PUBLISHED:
+  INLINE PStatThread(Thread *thread, PStatClient *client = NULL);
 
   INLINE PStatThread(const PStatThread &copy);
   INLINE void operator = (const PStatThread &copy);
 
   INLINE void new_frame();
 
+  Thread *get_thread() const;
+  INLINE int get_index() const;
+
 private:
   PStatClient *_client;
   int _index;

+ 1 - 0
panda/src/pstatclient/pstatclient_composite2.cxx

@@ -3,3 +3,4 @@
 #include "pStatFrameData.cxx"
 #include "pStatProperties.cxx"
 #include "pStatServerControlMessage.cxx"
+#include "pStatThread.cxx"