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

This commit is contained in:
David Rose 2006-01-31 18:59:49 +00:00
parent 0faf866c57
commit b7ceab1bff
24 changed files with 618 additions and 117 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#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() {
}

View File

@ -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
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
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);
int thread_index = thread->get_pstats_index();
if (thread_index != -1) {
return PStatThread((PStatClient *)this, thread_index);
}
// Create a new thread for this name.
MultiThingsByName::const_iterator ni =
_threads_by_name.find(thread->get_name());
if (ni != _threads_by_name.end()) {
// 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 PStatsThread for this thread pointer.
int new_index = _threads.size();
_threads_by_name.insert(ThingsByName::value_type(name, new_index));
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;
Thread thread;
thread._name = name;
thread._is_active = false;
thread._next_packet = 0.0;
thread._frame_number = 0;
_threads.push_back(thread);
_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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#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);
}

View File

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

View File

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