mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
better thread debugging
This commit is contained in:
parent
bce545c958
commit
448d244a66
@ -74,9 +74,11 @@ void ConditionVarDebug::
|
|||||||
wait() {
|
wait() {
|
||||||
_mutex._global_lock->lock();
|
_mutex._global_lock->lock();
|
||||||
|
|
||||||
|
Thread *this_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
if (!_mutex.do_debug_is_locked()) {
|
if (!_mutex.do_debug_is_locked()) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << *Thread::get_current_thread() << " attempted to wait on "
|
ostr << *this_thread << " attempted to wait on "
|
||||||
<< *this << " without holding " << _mutex;
|
<< *this << " without holding " << _mutex;
|
||||||
nassert_raise(ostr.str());
|
nassert_raise(ostr.str());
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -85,16 +87,25 @@ wait() {
|
|||||||
|
|
||||||
if (thread_cat->is_spam()) {
|
if (thread_cat->is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " waiting on " << *this << "\n";
|
<< *this_thread << " waiting on " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar == NULL &&
|
||||||
|
this_thread->_waiting_on_cvar_full == NULL) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar = this;
|
||||||
|
|
||||||
_mutex.do_release();
|
_mutex.do_release();
|
||||||
_impl.wait();
|
_impl.wait(); // temporarily releases _global_lock
|
||||||
_mutex.do_lock();
|
_mutex.do_lock();
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar == this) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar = NULL;
|
||||||
|
|
||||||
if (thread_cat.is_spam()) {
|
if (thread_cat.is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " awake on " << *this << "\n";
|
<< *this_thread << " awake on " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -115,9 +126,11 @@ void ConditionVarDebug::
|
|||||||
wait(double timeout) {
|
wait(double timeout) {
|
||||||
_mutex._global_lock->lock();
|
_mutex._global_lock->lock();
|
||||||
|
|
||||||
|
Thread *this_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
if (!_mutex.do_debug_is_locked()) {
|
if (!_mutex.do_debug_is_locked()) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << *Thread::get_current_thread() << " attempted to wait on "
|
ostr << *this_thread << " attempted to wait on "
|
||||||
<< *this << " without holding " << _mutex;
|
<< *this << " without holding " << _mutex;
|
||||||
nassert_raise(ostr.str());
|
nassert_raise(ostr.str());
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -126,17 +139,26 @@ wait(double timeout) {
|
|||||||
|
|
||||||
if (thread_cat.is_spam()) {
|
if (thread_cat.is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " waiting on " << *this
|
<< *this_thread << " waiting on " << *this
|
||||||
<< ", with timeout " << timeout << "\n";
|
<< ", with timeout " << timeout << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar == NULL &&
|
||||||
|
this_thread->_waiting_on_cvar_full == NULL) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar = this;
|
||||||
|
|
||||||
_mutex.do_release();
|
_mutex.do_release();
|
||||||
_impl.wait(timeout);
|
_impl.wait(timeout); // temporarily releases _global_lock
|
||||||
_mutex.do_lock();
|
_mutex.do_lock();
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar == this) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar = NULL;
|
||||||
|
|
||||||
if (thread_cat.is_spam()) {
|
if (thread_cat.is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " awake on " << *this << "\n";
|
<< *this_thread << " awake on " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -162,9 +184,12 @@ wait(double timeout) {
|
|||||||
void ConditionVarDebug::
|
void ConditionVarDebug::
|
||||||
signal() {
|
signal() {
|
||||||
_mutex._global_lock->lock();
|
_mutex._global_lock->lock();
|
||||||
|
|
||||||
|
Thread *this_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
if (!_mutex.do_debug_is_locked()) {
|
if (!_mutex.do_debug_is_locked()) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << *Thread::get_current_thread() << " attempted to signal "
|
ostr << *this_thread << " attempted to signal "
|
||||||
<< *this << " without holding " << _mutex;
|
<< *this << " without holding " << _mutex;
|
||||||
nassert_raise(ostr.str());
|
nassert_raise(ostr.str());
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -173,7 +198,7 @@ signal() {
|
|||||||
|
|
||||||
if (thread_cat->is_spam()) {
|
if (thread_cat->is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " signalling " << *this << "\n";
|
<< *this_thread << " signalling " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_impl.signal();
|
_impl.signal();
|
||||||
|
@ -74,9 +74,11 @@ void ConditionVarFullDebug::
|
|||||||
wait() {
|
wait() {
|
||||||
_mutex._global_lock->lock();
|
_mutex._global_lock->lock();
|
||||||
|
|
||||||
|
Thread *this_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
if (!_mutex.do_debug_is_locked()) {
|
if (!_mutex.do_debug_is_locked()) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << *Thread::get_current_thread() << " attempted to wait on "
|
ostr << *this_thread << " attempted to wait on "
|
||||||
<< *this << " without holding " << _mutex;
|
<< *this << " without holding " << _mutex;
|
||||||
nassert_raise(ostr.str());
|
nassert_raise(ostr.str());
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -85,16 +87,25 @@ wait() {
|
|||||||
|
|
||||||
if (thread_cat->is_spam()) {
|
if (thread_cat->is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " waiting on " << *this << "\n";
|
<< *this_thread << " waiting on " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar == NULL &&
|
||||||
|
this_thread->_waiting_on_cvar_full == NULL) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar_full = this;
|
||||||
|
|
||||||
_mutex.do_release();
|
_mutex.do_release();
|
||||||
_impl.wait();
|
_impl.wait(); // temporarily releases _global_lock
|
||||||
_mutex.do_lock();
|
_mutex.do_lock();
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar_full == this) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar_full = NULL;
|
||||||
|
|
||||||
if (thread_cat.is_spam()) {
|
if (thread_cat.is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " awake on " << *this << "\n";
|
<< *this_thread << " awake on " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -115,9 +126,11 @@ void ConditionVarFullDebug::
|
|||||||
wait(double timeout) {
|
wait(double timeout) {
|
||||||
_mutex._global_lock->lock();
|
_mutex._global_lock->lock();
|
||||||
|
|
||||||
|
Thread *this_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
if (!_mutex.do_debug_is_locked()) {
|
if (!_mutex.do_debug_is_locked()) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << *Thread::get_current_thread() << " attempted to wait on "
|
ostr << *this_thread << " attempted to wait on "
|
||||||
<< *this << " without holding " << _mutex;
|
<< *this << " without holding " << _mutex;
|
||||||
nassert_raise(ostr.str());
|
nassert_raise(ostr.str());
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -126,17 +139,26 @@ wait(double timeout) {
|
|||||||
|
|
||||||
if (thread_cat.is_spam()) {
|
if (thread_cat.is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " waiting on " << *this
|
<< *this_thread << " waiting on " << *this
|
||||||
<< ", with timeout " << timeout << "\n";
|
<< ", with timeout " << timeout << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar == NULL &&
|
||||||
|
this_thread->_waiting_on_cvar_full == NULL) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar_full = this;
|
||||||
|
|
||||||
_mutex.do_release();
|
_mutex.do_release();
|
||||||
_impl.wait(timeout);
|
_impl.wait(timeout); // temporarily releases _global_lock
|
||||||
_mutex.do_lock();
|
_mutex.do_lock();
|
||||||
|
|
||||||
|
nassertd(this_thread->_waiting_on_cvar_full == this) {
|
||||||
|
}
|
||||||
|
this_thread->_waiting_on_cvar_full = NULL;
|
||||||
|
|
||||||
if (thread_cat.is_spam()) {
|
if (thread_cat.is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " awake on " << *this << "\n";
|
<< *this_thread << " awake on " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -162,9 +184,12 @@ wait(double timeout) {
|
|||||||
void ConditionVarFullDebug::
|
void ConditionVarFullDebug::
|
||||||
signal() {
|
signal() {
|
||||||
_mutex._global_lock->lock();
|
_mutex._global_lock->lock();
|
||||||
|
|
||||||
|
Thread *this_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
if (!_mutex.do_debug_is_locked()) {
|
if (!_mutex.do_debug_is_locked()) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << *Thread::get_current_thread() << " attempted to signal "
|
ostr << *this_thread << " attempted to signal "
|
||||||
<< *this << " without holding " << _mutex;
|
<< *this << " without holding " << _mutex;
|
||||||
nassert_raise(ostr.str());
|
nassert_raise(ostr.str());
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -173,7 +198,7 @@ signal() {
|
|||||||
|
|
||||||
if (thread_cat->is_spam()) {
|
if (thread_cat->is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " signalling " << *this << "\n";
|
<< *this_thread << " signalling " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_impl.signal();
|
_impl.signal();
|
||||||
@ -197,9 +222,12 @@ signal() {
|
|||||||
void ConditionVarFullDebug::
|
void ConditionVarFullDebug::
|
||||||
signal_all() {
|
signal_all() {
|
||||||
_mutex._global_lock->lock();
|
_mutex._global_lock->lock();
|
||||||
|
|
||||||
|
Thread *this_thread = Thread::get_current_thread();
|
||||||
|
|
||||||
if (!_mutex.do_debug_is_locked()) {
|
if (!_mutex.do_debug_is_locked()) {
|
||||||
ostringstream ostr;
|
ostringstream ostr;
|
||||||
ostr << *Thread::get_current_thread() << " attempted to signal "
|
ostr << *this_thread << " attempted to signal "
|
||||||
<< *this << " without holding " << _mutex;
|
<< *this << " without holding " << _mutex;
|
||||||
nassert_raise(ostr.str());
|
nassert_raise(ostr.str());
|
||||||
_mutex._global_lock->release();
|
_mutex._global_lock->release();
|
||||||
@ -208,7 +236,7 @@ signal_all() {
|
|||||||
|
|
||||||
if (thread_cat->is_spam()) {
|
if (thread_cat->is_spam()) {
|
||||||
thread_cat.spam()
|
thread_cat.spam()
|
||||||
<< *Thread::get_current_thread() << " signalling all " << *this << "\n";
|
<< *this_thread << " signalling all " << *this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
_impl.signal_all();
|
_impl.signal_all();
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
#include "mainThread.h"
|
#include "mainThread.h"
|
||||||
#include "externalThread.h"
|
#include "externalThread.h"
|
||||||
#include "config_pipeline.h"
|
#include "config_pipeline.h"
|
||||||
|
#include "mutexDebug.h"
|
||||||
|
#include "conditionVarDebug.h"
|
||||||
|
#include "conditionVarFullDebug.h"
|
||||||
|
|
||||||
Thread *Thread::_main_thread;
|
Thread *Thread::_main_thread;
|
||||||
Thread *Thread::_external_thread;
|
Thread *Thread::_external_thread;
|
||||||
@ -52,6 +55,8 @@ Thread(const string &name, const string &sync_name) :
|
|||||||
|
|
||||||
#ifdef DEBUG_THREADS
|
#ifdef DEBUG_THREADS
|
||||||
_blocked_on_mutex = NULL;
|
_blocked_on_mutex = NULL;
|
||||||
|
_waiting_on_cvar = NULL;
|
||||||
|
_waiting_on_cvar_full = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_PYTHON) && !defined(SIMPLE_THREADS)
|
#if defined(HAVE_PYTHON) && !defined(SIMPLE_THREADS)
|
||||||
@ -69,7 +74,9 @@ Thread(const string &name, const string &sync_name) :
|
|||||||
Thread::
|
Thread::
|
||||||
~Thread() {
|
~Thread() {
|
||||||
#ifdef DEBUG_THREADS
|
#ifdef DEBUG_THREADS
|
||||||
nassertv(_blocked_on_mutex == NULL);
|
nassertv(_blocked_on_mutex == NULL &&
|
||||||
|
_waiting_on_cvar == NULL &&
|
||||||
|
_waiting_on_cvar_full == NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +160,27 @@ output(ostream &out) const {
|
|||||||
out << get_type() << " " << get_name();
|
out << get_type() << " " << get_name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: Thread::output_blocker
|
||||||
|
// Access: Published
|
||||||
|
// Description: Writes a description of the mutex or condition
|
||||||
|
// variable that this thread is blocked on. Writes
|
||||||
|
// nothing if there is no blocker, or if we are not in
|
||||||
|
// DEBUG_THREADS mode.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void Thread::
|
||||||
|
output_blocker(ostream &out) const {
|
||||||
|
#ifdef DEBUG_THREADS
|
||||||
|
if (_blocked_on_mutex != (MutexDebug *)NULL) {
|
||||||
|
out << *_blocked_on_mutex;
|
||||||
|
} else if (_waiting_on_cvar != (ConditionVarDebug *)NULL) {
|
||||||
|
out << *_waiting_on_cvar;
|
||||||
|
} else if (_waiting_on_cvar_full != (ConditionVarFullDebug *)NULL) {
|
||||||
|
out << *_waiting_on_cvar_full;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_THREADS
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: Thread::write_status
|
// Function: Thread::write_status
|
||||||
// Access: Published, Static
|
// Access: Published, Static
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
class Mutex;
|
class Mutex;
|
||||||
class ReMutex;
|
class ReMutex;
|
||||||
class MutexDebug;
|
class MutexDebug;
|
||||||
|
class ConditionVarDebug;
|
||||||
|
class ConditionVarFullDebug;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Class : Thread
|
// Class : Thread
|
||||||
@ -83,6 +85,7 @@ PUBLISHED:
|
|||||||
BLOCKING INLINE static void consider_yield();
|
BLOCKING INLINE static void consider_yield();
|
||||||
|
|
||||||
virtual void output(ostream &out) const;
|
virtual void output(ostream &out) const;
|
||||||
|
void output_blocker(ostream &out) const;
|
||||||
static void write_status(ostream &out);
|
static void write_status(ostream &out);
|
||||||
|
|
||||||
INLINE bool is_started() const;
|
INLINE bool is_started() const;
|
||||||
@ -130,7 +133,9 @@ private:
|
|||||||
|
|
||||||
#ifdef DEBUG_THREADS
|
#ifdef DEBUG_THREADS
|
||||||
MutexDebug *_blocked_on_mutex;
|
MutexDebug *_blocked_on_mutex;
|
||||||
#endif
|
ConditionVarDebug *_waiting_on_cvar;
|
||||||
|
ConditionVarFullDebug *_waiting_on_cvar_full;
|
||||||
|
#endif // DEBUG_THREADS
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Thread *_main_thread;
|
static Thread *_main_thread;
|
||||||
@ -154,6 +159,8 @@ private:
|
|||||||
static TypeHandle _type_handle;
|
static TypeHandle _type_handle;
|
||||||
|
|
||||||
friend class MutexDebug;
|
friend class MutexDebug;
|
||||||
|
friend class ConditionVarDebug;
|
||||||
|
friend class ConditionVarFullDebug;
|
||||||
|
|
||||||
friend class ThreadDummyImpl;
|
friend class ThreadDummyImpl;
|
||||||
friend class ThreadWin32Impl;
|
friend class ThreadWin32Impl;
|
||||||
|
@ -334,6 +334,9 @@ write_status(ostream &out) const {
|
|||||||
for (ti = _ready.begin(); ti != _ready.end(); ++ti) {
|
for (ti = _ready.begin(); ti != _ready.end(); ++ti) {
|
||||||
out << " " << *(*ti)->_parent_obj;
|
out << " " << *(*ti)->_parent_obj;
|
||||||
}
|
}
|
||||||
|
for (ti = _next_ready.begin(); ti != _next_ready.end(); ++ti) {
|
||||||
|
out << " " << *(*ti)->_parent_obj;
|
||||||
|
}
|
||||||
out << "\n";
|
out << "\n";
|
||||||
|
|
||||||
double now = get_current_time();
|
double now = get_current_time();
|
||||||
@ -357,6 +360,11 @@ write_status(ostream &out) const {
|
|||||||
for (ti = threads.begin(); ti != threads.end(); ++ti) {
|
for (ti = threads.begin(); ti != threads.end(); ++ti) {
|
||||||
ThreadSimpleImpl *thread = (*ti);
|
ThreadSimpleImpl *thread = (*ti);
|
||||||
out << " " << *thread->_parent_obj;
|
out << " " << *thread->_parent_obj;
|
||||||
|
#ifdef DEBUG_THREADS
|
||||||
|
out << " (";
|
||||||
|
thread->_parent_obj->output_blocker(out);
|
||||||
|
out << ")";
|
||||||
|
#endif // DEBUG_THREADS
|
||||||
}
|
}
|
||||||
out << "\n";
|
out << "\n";
|
||||||
}
|
}
|
||||||
@ -527,17 +535,23 @@ wake_sleepers(double now) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void ThreadSimpleManager::
|
void ThreadSimpleManager::
|
||||||
report_deadlock() {
|
report_deadlock() {
|
||||||
Blocked::iterator bi;
|
Blocked::const_iterator bi;
|
||||||
for (bi = _blocked.begin(); bi != _blocked.end(); ++bi) {
|
for (bi = _blocked.begin(); bi != _blocked.end(); ++bi) {
|
||||||
BlockerSimple *blocker = (*bi).first;
|
BlockerSimple *blocker = (*bi).first;
|
||||||
FifoThreads &threads = (*bi).second;
|
const FifoThreads &threads = (*bi).second;
|
||||||
thread_cat.info()
|
thread_cat.info()
|
||||||
<< "On blocker " << blocker << ":\n";
|
<< "On blocker " << blocker << ":\n";
|
||||||
FifoThreads::iterator ti;
|
FifoThreads::const_iterator ti;
|
||||||
for (ti = threads.begin(); ti != threads.end(); ++ti) {
|
for (ti = threads.begin(); ti != threads.end(); ++ti) {
|
||||||
ThreadSimpleImpl *thread = (*ti);
|
ThreadSimpleImpl *thread = (*ti);
|
||||||
thread_cat.info()
|
thread_cat.info()
|
||||||
<< " " << *thread->_parent_obj << "\n";
|
<< " " << *thread->_parent_obj;
|
||||||
|
#ifdef DEBUG_THREADS
|
||||||
|
thread_cat.info(false) << " (";
|
||||||
|
thread->_parent_obj->output_blocker(thread_cat.info(false));
|
||||||
|
thread_cat.info(false) << ")";
|
||||||
|
#endif // DEBUG_THREADS
|
||||||
|
thread_cat.info(false) << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user