Browse Source

robustify threading some more

David Rose 20 years ago
parent
commit
1123896fc6

+ 12 - 3
panda/src/display/graphicsEngine.cxx

@@ -578,6 +578,7 @@ open_windows() {
   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) {
       thread->_thread_state = TS_do_windows;
       thread->_cv.signal();
@@ -589,6 +590,7 @@ open_windows() {
   // window.
   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();
@@ -679,6 +681,7 @@ bool GraphicsEngine::
 add_callback(const string &thread_name, 
              GraphicsEngine::CallbackTime callback_time,
              GraphicsEngine::CallbackFunction *func, void *data) {
+  MutexHolder holder(_lock);
   WindowRenderer *wr = get_window_renderer(thread_name);
   return wr->add_callback(callback_time, Callback(func, data));
 }
@@ -699,6 +702,7 @@ bool GraphicsEngine::
 remove_callback(const string &thread_name, 
                 GraphicsEngine::CallbackTime callback_time,
                 GraphicsEngine::CallbackFunction *func, void *data) {
+  MutexHolder holder(_lock);
   WindowRenderer *wr = get_window_renderer(thread_name);
   return wr->remove_callback(callback_time, Callback(func, data));
 }
@@ -941,6 +945,8 @@ flip_windows(const GraphicsEngine::Windows &wlist) {
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 do_sync_frame() {
+  nassertv(_lock.debug_is_locked());
+
   // Statistics
   PStatTimer timer(_sync_pcollector);
 
@@ -966,6 +972,8 @@ do_sync_frame() {
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 do_flip_frame() {
+  nassertv(_lock.debug_is_locked());
+
   // Statistics
   PStatTimer timer(_flip_pcollector);
 
@@ -1312,7 +1320,6 @@ terminate_threads() {
   // Now tell them to close their windows and terminate.
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
-    MutexHolder cv_holder(thread->_cv_mutex);
     thread->_thread_state = TS_terminate;
     thread->_cv.signal();
     thread->_cv_mutex.release();
@@ -1352,15 +1359,17 @@ get_invert_polygon_state() {
 //       Access: Private
 //  Description: Returns the WindowRenderer with the given name.
 //               Creates a new RenderThread if there is no such thread
-//               already.
+//               already.  You must already be holding the lock before
+//               calling this method.
 ////////////////////////////////////////////////////////////////////
 GraphicsEngine::WindowRenderer *GraphicsEngine::
 get_window_renderer(const string &name) {
+  nassertr(_lock.debug_is_locked(), NULL);
+
   if (name.empty()) {
     return &_app;
   }
 
-  MutexHolder holder(_lock);
   Threads::iterator ti = _threads.find(name);
   if (ti != _threads.end()) {
     return (*ti).second.p();

+ 11 - 1
panda/src/express/Sources.pp

@@ -29,6 +29,7 @@
     hashVal.I hashVal.h \
     indirectLess.I indirectLess.h \
     littleEndian.h \
+    mainThread.h \
     memoryInfo.I memoryInfo.h \
     memoryUsage.I memoryUsage.h \
     memoryUsagePointerCounts.I memoryUsagePointerCounts.h \
@@ -50,6 +51,8 @@
     pta_uchar.h \
     ramfile.I ramfile.h \
     referenceCount.I referenceCount.h \
+    reMutex.I reMutex.h \
+    reMutexHolder.I reMutexHolder.h \
     reversedNumericData.I reversedNumericData.h \
     selectThreadImpl.h \
     streamReader.I streamReader.h streamWriter.I streamWriter.h \
@@ -86,6 +89,7 @@
     encryptStreamBuf.cxx encryptStream.cxx \
     error_utils.cxx \
     hashGeneratorBase.cxx hashVal.cxx \
+    mainThread.cxx \
     memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
     memoryUsagePointers.cxx multifile.cxx \
     pmutex.cxx mutexHolder.cxx mutexDummyImpl.cxx mutexNsprImpl.cxx \
@@ -102,6 +106,8 @@
     pta_uchar.cxx \
     ramfile.cxx \
     referenceCount.cxx \
+    reMutex.cxx \
+    reMutexHolder.cxx \
     reversedNumericData.cxx \
     streamReader.cxx streamWriter.cxx \
     stringDecoder.cxx \
@@ -142,7 +148,9 @@
     hashGeneratorBase.I \
     hashGeneratorBase.h hashVal.I hashVal.h \
     indirectLess.I indirectLess.h \
-    littleEndian.h memoryInfo.I memoryInfo.h memoryUsage.I \
+    littleEndian.h \
+    mainThread.h \
+    memoryInfo.I memoryInfo.h memoryUsage.I \
     memoryUsage.h memoryUsagePointerCounts.I \
     memoryUsagePointerCounts.h memoryUsagePointers.I \
     memoryUsagePointers.h multifile.I multifile.h \
@@ -161,6 +169,8 @@
     profileTimer.h pta_uchar.h \
     ramfile.I ramfile.h \
     referenceCount.I referenceCount.h \
+    reMutex.I reMutex.h \
+    reMutexHolder.I reMutexHolder.h \
     reversedNumericData.I reversedNumericData.h \
     selectThreadImpl.h \
     streamReader.I streamReader.h streamWriter.I streamWriter.h \

+ 7 - 0
panda/src/express/conditionVar.I

@@ -104,7 +104,12 @@ get_mutex() {
 ////////////////////////////////////////////////////////////////////
 INLINE void ConditionVar::
 wait() {
+  nassertv(_mutex._locking_thread == Thread::get_current_thread());
   _impl.wait();
+
+#ifndef NDEBUG
+  _mutex._locking_thread = Thread::get_current_thread();
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -125,6 +130,7 @@ wait() {
 ////////////////////////////////////////////////////////////////////
 INLINE void ConditionVar::
 signal() {
+  nassertv(_mutex._locking_thread == Thread::get_current_thread());
   _impl.signal();
 }
 
@@ -140,5 +146,6 @@ signal() {
 ////////////////////////////////////////////////////////////////////
 INLINE void ConditionVar::
 signal_all() {
+  nassertv(_mutex._locking_thread == Thread::get_current_thread());
   _impl.signal_all();
 }

+ 1 - 0
panda/src/express/conditionVar.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "pmutex.h"
 #include "conditionVarImpl.h"
+#include "thread.h"
 #include "notify.h"
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 0
panda/src/express/config_express.cxx

@@ -19,6 +19,7 @@
 
 #include "config_express.h"
 #include "datagram.h"
+#include "mainThread.h"
 #include "referenceCount.h"
 #include "textEncoder.h"
 #include "thread.h"
@@ -158,6 +159,7 @@ init_libexpress() {
   initialized = true;
 
   Datagram::init_type();
+  MainThread::init_type();
   ReferenceCount::init_type();
   TextEncoder::init_type();
   Thread::init_type();

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

@@ -18,6 +18,7 @@
 #include "encryptStream.cxx"
 #include "hashGeneratorBase.cxx"
 #include "hashVal.cxx"
+#include "mainThread.cxx"
 #include "memoryInfo.cxx"
 #include "memoryUsage.cxx"
 #include "memoryUsagePointerCounts.cxx"

+ 2 - 0
panda/src/express/express_composite2.cxx

@@ -9,6 +9,8 @@
 #include "pta_uchar.cxx"
 #include "ramfile.cxx"
 #include "referenceCount.cxx"
+#include "reMutex.cxx"
+#include "reMutexHolder.cxx"
 #include "reversedNumericData.cxx"
 #include "streamReader.cxx"
 #include "streamWriter.cxx"

+ 39 - 0
panda/src/express/mainThread.cxx

@@ -0,0 +1,39 @@
+// Filename: mainThread.cxx
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "mainThread.h"
+
+TypeHandle MainThread::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MainThread::Constructor
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MainThread::
+MainThread() : Thread("main") {
+}
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: MainThread::thread_main
+//       Access: Private, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void MainThread::
+thread_main() {
+}

+ 52 - 0
panda/src/express/mainThread.h

@@ -0,0 +1,52 @@
+// Filename: mainThread.h
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 MAINTHREAD_H
+#define MAINTHREAD_H
+
+#include "pandabase.h"
+#include "thread.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : MainThread
+// Description : The special "main thread" class.  There is one
+//               instance of these in the world, and it is returned by
+//               Thread::get_main_thread().
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS MainThread : public Thread {
+private:
+  MainThread();
+  virtual void thread_main();
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    Thread::init_type();
+    register_type(_type_handle, "MainThread",
+                  Thread::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class Thread;
+};
+
+#endif

+ 20 - 0
panda/src/express/pmutex.I

@@ -72,7 +72,17 @@ operator = (const Mutex &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE void Mutex::
 lock() const {
+#ifdef NDEBUG
+  // In the NDEBUG case, just lock the thing immediately.  Don't
+  // bother with the out-of-line do_lock() method, since we won't be
+  // performing any checks anyway.
   ((MutexImpl &)_impl).lock();
+
+#else 
+  // In development mode, call the out-of-line do_lock() method, which
+  // might perform an additional check or two.
+  ((Mutex *)this)->do_lock();
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -87,5 +97,15 @@ lock() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void Mutex::
 release() const {
+#ifdef NDEBUG
+  // In the NDEBUG case, just release the thing immediately.  Don't
+  // bother with the out-of-line do_release() method, since we won't
+  // be performing any checks anyway.
   ((MutexImpl &)_impl).release();
+
+#else 
+  // In development mode, call the out-of-line do_release() method,
+  // which might perform an additional check or two.
+  ((Mutex *)this)->do_release();
+#endif
 }

+ 46 - 0
panda/src/express/pmutex.cxx

@@ -17,3 +17,49 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "pmutex.h"
+#include "thread.h"
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: Mutex::debug_is_locked
+//       Access: Public
+//  Description: Returns true if the current thread has locked the
+//               Mutex, false otherwise.  This method only exists in
+//               !NDEBUG mode, so it's only appropriate to call it
+//               from within an assert().
+////////////////////////////////////////////////////////////////////
+bool Mutex::
+debug_is_locked() const {
+  return (_locking_thread == Thread::get_current_thread());
+}
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: Mutex::do_lock
+//       Access: Private
+//  Description: The private implementation of lock().
+////////////////////////////////////////////////////////////////////
+void Mutex::
+do_lock() {
+  nassertv(_locking_thread != Thread::get_current_thread());
+  _impl.lock();
+
+#ifndef NDEBUG
+  _locking_thread = Thread::get_current_thread();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Mutex::do_release
+//       Access: Private
+//  Description: The private implementation of release().
+////////////////////////////////////////////////////////////////////
+void Mutex::
+do_release() {
+  nassertv(_locking_thread == Thread::get_current_thread());
+#ifndef NDEBUG
+  _locking_thread = (Thread *)NULL;
+#endif
+
+  _impl.release();
+}

+ 17 - 1
panda/src/express/pmutex.h

@@ -21,7 +21,8 @@
 
 #include "pandabase.h"
 #include "mutexImpl.h"
-#include "notify.h"
+
+class Thread;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Mutex
@@ -42,8 +43,23 @@ public:
   INLINE void lock() const;
   INLINE void release() const;
 
+#ifndef NDEBUG
+  bool debug_is_locked() const;
+#endif
+
+private:
+  void do_lock();
+  void do_release();
+
 private:
   MutexImpl _impl;
+
+#ifndef NDEBUG
+  // Make sure that ordinary mutexes are not locked reentrantly
+  // (that's what a ReMutex is for).
+  Thread *_locking_thread;
+#endif
+
   friend class ConditionVar;
 };
 

+ 123 - 0
panda/src/express/reMutex.I

@@ -0,0 +1,123 @@
+// Filename: reMutex.I
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutex::
+ReMutex() : _cvar(_mutex) {
+  _locking_thread = NULL;
+  _lock_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutex::
+~ReMutex() {
+  nassertv(_locking_thread == NULL && _lock_count == 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy mutexes.
+////////////////////////////////////////////////////////////////////
+INLINE ReMutex::
+ReMutex(const ReMutex &copy) : _cvar(_mutex) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy mutexes.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutex::
+operator = (const ReMutex &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::lock
+//       Access: Public
+//  Description: Grabs the mutex if it is available.  If it is not
+//               available, blocks until it becomes available, then
+//               grabs it.  In either case, the function does not
+//               return until the mutex is held; you should then call
+//               unlock().
+//
+//               This method is considered const so that you can lock
+//               and unlock const mutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+//
+//               Also see ReMutexHolder.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutex::
+lock() const {
+  ((ReMutex *)this)->do_lock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::release
+//       Access: Public
+//  Description: Releases the mutex.  It is an error to call this if
+//               the mutex was not already locked.
+//
+//               This method is considered const so that you can lock
+//               and unlock const mutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutex::
+release() const {
+  ((ReMutex *)this)->do_release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::is_locked
+//       Access: Public
+//  Description: Returns true if *this thread* already holds the lock,
+//               false if no one holds the lock or the lock is held by
+//               some other thread.
+////////////////////////////////////////////////////////////////////
+INLINE bool ReMutex::
+is_locked() const {
+  MutexHolder holder(_mutex);
+  return (_locking_thread == Thread::get_current_thread());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::get_lock_count
+//       Access: Public
+//  Description: Returns the number of times that *this thread* has
+//               grabbed the lock.  Returns 0 if some other thread is
+//               currently holding the lock.
+////////////////////////////////////////////////////////////////////
+INLINE int ReMutex::
+get_lock_count() const {
+  MutexHolder holder(_mutex);
+  if (_locking_thread == Thread::get_current_thread()) {
+    return _lock_count;
+  }
+  return 0;
+}

+ 71 - 0
panda/src/express/reMutex.cxx

@@ -0,0 +1,71 @@
+// Filename: reMutex.cxx
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "reMutex.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::do_lock
+//       Access: Private
+//  Description: The private implementation of lock().
+////////////////////////////////////////////////////////////////////
+void ReMutex::
+do_lock() {
+  MutexHolder holder(_mutex);
+
+  if (_locking_thread == (Thread *)NULL) {
+    // The mutex is not already locked by anyone.  Lock it.
+    _locking_thread = Thread::get_current_thread();
+    ++_lock_count;
+    nassertv(_lock_count == 1);
+
+  } else if (_locking_thread == Thread::get_current_thread()) {
+    // The mutex is already locked by this thread.  Increment the lock
+    // count.
+    ++_lock_count;
+
+  } else {
+    // The mutex is locked by some other thread.  Go to sleep on the
+    // condition variable until it's unlocked.
+    while (_locking_thread != (Thread *)NULL) {
+      _cvar.wait();
+    }
+    _locking_thread = Thread::get_current_thread();
+    ++_lock_count;
+    nassertv(_lock_count == 1);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutex::do_release
+//       Access: Private
+//  Description: The private implementation of release().
+////////////////////////////////////////////////////////////////////
+void ReMutex::
+do_release() {
+  MutexHolder holder(_mutex);
+
+  nassertv(_locking_thread == Thread::get_current_thread());
+  nassertv(_lock_count > 0);
+
+  --_lock_count;
+  if (_lock_count == 0) {
+    // That was the last lock held by this thread.  Release the lock.
+    _locking_thread = (Thread *)NULL;
+    _cvar.signal();
+  }
+}

+ 63 - 0
panda/src/express/reMutex.h

@@ -0,0 +1,63 @@
+// Filename: reMutex.h
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 REMUTEX_H
+#define REMUTEX_H
+
+#include "pandabase.h"
+#include "pmutex.h"
+#include "conditionVar.h"
+#include "mutexHolder.h"
+#include "thread.h"
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : ReMutex
+// Description : A reentrant mutex.  This kind of mutex can be locked
+//               again by the thread that already holds it, without
+//               deadlock.  The thread must eventually release the
+//               mutex the same number of times it locked it.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS ReMutex {
+public:
+  INLINE ReMutex();
+  INLINE ~ReMutex();
+private:
+  INLINE ReMutex(const ReMutex &copy);
+  INLINE void operator = (const ReMutex &copy);
+
+public:
+  INLINE void lock() const;
+  INLINE void release() const;
+
+  INLINE bool is_locked() const;
+  INLINE int get_lock_count() const;
+
+private:
+  void do_lock();
+  void do_release();
+
+  Mutex _mutex;
+  ConditionVar _cvar;
+  Thread *_locking_thread;
+  int _lock_count;
+};
+
+#include "reMutex.I"
+
+#endif

+ 85 - 0
panda/src/express/reMutexHolder.I

@@ -0,0 +1,85 @@
+// Filename: reMutexHolder.I
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexHolder::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexHolder::
+ReMutexHolder(const ReMutex &mutex) {
+#if defined(HAVE_THREADS) || !defined(NDEBUG)
+  _mutex = &mutex;
+  _mutex->lock();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexHolder::Constructor
+//       Access: Public
+//  Description: If the ReMutexHolder constructor is given a pointer to
+//               a ReMutex object (instead of an actual object), it will
+//               first check to see if the pointer is NULL, and
+//               allocate a new ReMutex if it is.  This is intended as a
+//               convenience for functions that may need to reference
+//               a ReMutex at static init time, when it is impossible to
+//               guarantee ordering of initializers.
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexHolder::
+ReMutexHolder(ReMutex *&mutex) {
+#if defined(HAVE_THREADS) || !defined(NDEBUG)
+  if (mutex == (ReMutex *)NULL) {
+    mutex = new ReMutex;
+  }
+  _mutex = mutex;
+  _mutex->lock();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexHolder::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexHolder::
+~ReMutexHolder() {
+#if defined(HAVE_THREADS) || !defined(NDEBUG)
+  _mutex->release();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexHolder::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy ReMutexHolders.
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexHolder::
+ReMutexHolder(const ReMutexHolder &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexHolder::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy ReMutexHolders.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexHolder::
+operator = (const ReMutexHolder &copy) {
+  nassertv(false);
+}

+ 19 - 0
panda/src/express/reMutexHolder.cxx

@@ -0,0 +1,19 @@
+// Filename: reMutexHolder.cxx
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "reMutexHolder.h"

+ 46 - 0
panda/src/express/reMutexHolder.h

@@ -0,0 +1,46 @@
+// Filename: reMutexHolder.h
+// Created by:  drose (15Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 REMUTEXHOLDER_H
+#define REMUTEXHOLDER_H
+
+#include "pandabase.h"
+#include "reMutex.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ReMutexHolder
+// Description : Similar to MutexHolder, but for a reentrant mutex.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS ReMutexHolder {
+public:
+  INLINE ReMutexHolder(const ReMutex &mutex);
+  INLINE ReMutexHolder(ReMutex *&mutex);
+  INLINE ~ReMutexHolder();
+private:
+  INLINE ReMutexHolder(const ReMutexHolder &copy);
+  INLINE void operator = (const ReMutexHolder &copy);
+
+private:
+#if defined(HAVE_THREADS) || !defined(NDEBUG)
+  const ReMutex *_mutex;
+#endif
+};
+
+#include "reMutexHolder.I"
+
+#endif

+ 18 - 3
panda/src/express/thread.I

@@ -133,13 +133,28 @@ prepare_for_exit() {
   ThreadImpl::prepare_for_exit();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::get_main_thread
+//       Access: Public, Static
+//  Description: Returns a pointer to the "main" Thread object--this
+//               is the Thread that started the whole process.
+////////////////////////////////////////////////////////////////////
+INLINE Thread *Thread::
+get_main_thread() {
+  if (_main_thread == (Thread *)NULL) {
+    init_main_thread();
+  }
+  return _main_thread;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_current_thread
 //       Access: Public, Static
 //  Description: Returns a pointer to the currently-executing Thread
-//               object, or NULL if the main thread (or some system
-//               thread other than one started from the Panda
-//               interface) is currently executing.
+//               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.
 ////////////////////////////////////////////////////////////////////
 INLINE Thread *Thread::
 get_current_thread() {

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

@@ -17,7 +17,9 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "thread.h"
+#include "mainThread.h"
 
+Thread *Thread::_main_thread;
 TypeHandle Thread::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -30,7 +32,7 @@ Thread::
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Thread::Destructor
+//     Function: Thread::output
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -38,3 +40,17 @@ void Thread::
 output(ostream &out) const {
   out << get_type() << " " << get_name();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::init_main_thread
+//       Access: Private, Static
+//  Description: Creates the Thread object that represents the main
+//               thread.
+////////////////////////////////////////////////////////////////////
+void Thread::
+init_main_thread() {
+  if (_main_thread == (Thread *)NULL) {
+    _main_thread = new MainThread;
+    ((Thread *)_main_thread)->_started = true;
+  }
+}

+ 7 - 2
panda/src/express/thread.h

@@ -58,6 +58,7 @@ public:
 
   INLINE static void prepare_for_exit();
 
+  INLINE static Thread *get_main_thread();
   INLINE static Thread *get_current_thread();
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
@@ -65,12 +66,13 @@ public:
   virtual void output(ostream &out) const;
 
 private:
+  static void init_main_thread();
+
   bool _started;
   string _name;
   ThreadImpl _impl;
-  friend class ThreadDummyImpl;
-  friend class ThreadNsprImpl;
 
+  static Thread *_main_thread;
 
 public:
   static TypeHandle get_class_type() {
@@ -84,6 +86,9 @@ public:
 
 private:
   static TypeHandle _type_handle;
+
+  friend class ThreadDummyImpl;
+  friend class ThreadNsprImpl;
 };
 
 INLINE ostream &operator << (ostream &out, const Thread &thread);

+ 0 - 10
panda/src/express/threadDummyImpl.I

@@ -72,16 +72,6 @@ INLINE void ThreadDummyImpl::
 prepare_for_exit() {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: ThreadDummyImpl::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE Thread *ThreadDummyImpl::
-get_current_thread() {
-  return NULL;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadDummyImpl::is_threading_supported
 //       Access: Public, Static

+ 11 - 0
panda/src/express/threadDummyImpl.cxx

@@ -21,5 +21,16 @@
 #ifdef THREAD_DUMMY_IMPL
 
 #include "threadDummyImpl.h"
+#include "thread.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadDummyImpl::get_current_thread
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Thread *ThreadDummyImpl::
+get_current_thread() {
+  return Thread::get_main_thread();
+}
 
 #endif  // THREAD_DUMMY_IMPL

+ 1 - 2
panda/src/express/threadDummyImpl.h

@@ -26,7 +26,6 @@
 
 #include "notify.h"
 #include "threadPriority.h"
-#include "pmutex.h"
 
 class Thread;
 
@@ -53,7 +52,7 @@ public:
 
   INLINE static void prepare_for_exit();
 
-  INLINE static Thread *get_current_thread();
+  static Thread *get_current_thread();
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
 

+ 1 - 1
panda/src/express/threadNsprImpl.I

@@ -48,7 +48,7 @@ prepare_for_exit() {
 INLINE Thread *ThreadNsprImpl::
 get_current_thread() {
   if (!_got_pt_ptr_index) {
-    return (Thread *)NULL;
+    init_pt_ptr_index();
   }
   return (Thread *)PR_GetThreadPrivate(_pt_ptr_index);
 }

+ 27 - 10
panda/src/express/threadNsprImpl.cxx

@@ -97,16 +97,7 @@ start(ThreadPriority priority, bool global, bool joinable) {
   _joinable = joinable;
 
   if (!_got_pt_ptr_index) {
-    // Allocate a new index to store the Thread parent pointer as a
-    // piece of per-thread private data.
-    PRStatus result = PR_NewThreadPrivateIndex(&_pt_ptr_index, NULL);
-    if (result == PR_SUCCESS) {
-      _got_pt_ptr_index = true;
-    } else {
-      thread_cat.error()
-        << "Unable to associate Thread pointers with threads.\n";
-      return false;
-    }
+    init_pt_ptr_index();
   }
 
   PRThreadPriority nspr_pri;
@@ -205,4 +196,30 @@ root_func(void *data) {
   unref_delete(parent_obj);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadNsprImpl::init_pt_ptr_index
+//       Access: Private, Static
+//  Description: Allocate a new index to store the Thread parent
+//               pointer as a piece of per-thread private data.
+////////////////////////////////////////////////////////////////////
+void ThreadNsprImpl::
+init_pt_ptr_index() {
+  nassertv(!_got_pt_ptr_index);
+
+  PRStatus result = PR_NewThreadPrivateIndex(&_pt_ptr_index, NULL);
+  if (result != PR_SUCCESS) {
+    thread_cat.error()
+      << "Unable to associate Thread pointers with threads.\n";
+    return;
+  }
+
+  _got_pt_ptr_index = true;
+
+  // Assume that we must be in the main thread, since this method must
+  // be called before the first thread is spawned.
+  Thread *main_thread_obj = Thread::get_main_thread();
+  result = PR_SetThreadPrivate(_pt_ptr_index, main_thread_obj);
+  nassertv(result == PR_SUCCESS);
+}
+
 #endif  // THREAD_NSPR_IMPL

+ 1 - 1
panda/src/express/threadNsprImpl.h

@@ -54,7 +54,7 @@ public:
 
 private:
   static void root_func(void *data);
-  static void pt_ptr_destructor(void *data);
+  static void init_pt_ptr_index();
 
   Mutex _mutex;
   Thread *_parent_obj;