소스 검색

first pass at SIMPLE_THREADS

David Rose 18 년 전
부모
커밋
8d2bdc39fd
69개의 변경된 파일2476개의 추가작업 그리고 137개의 파일을 삭제
  1. 37 7
      dtool/Config.pp
  2. 13 0
      dtool/LocalSetup.pp
  3. 1 1
      dtool/src/dtoolbase/atomicAdjust.h
  4. 1 1
      dtool/src/dtoolbase/mutexImpl.h
  5. 6 0
      dtool/src/dtoolbase/selectThreadImpl.h
  6. 12 0
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  7. 1 0
      dtool/src/parser-inc/Python.h
  8. 18 0
      dtool/src/parser-inc/queue
  9. 4 0
      dtool/src/pystub/pystub.cxx
  10. 4 4
      panda/src/display/graphicsEngine.cxx
  11. 1 1
      panda/src/event/eventHandler.cxx
  12. 1 1
      panda/src/event/eventHandler.h
  13. 43 0
      panda/src/express/trueClock.I
  14. 4 17
      panda/src/express/trueClock.cxx
  15. 6 1
      panda/src/express/trueClock.h
  16. 2 0
      panda/src/framework/pandaFramework.cxx
  17. 2 0
      panda/src/gobj/vertexDataPage.cxx
  18. 58 9
      panda/src/pipeline/Sources.pp
  19. 38 0
      panda/src/pipeline/blockerSimple.I
  20. 57 0
      panda/src/pipeline/blockerSimple.h
  21. 1 1
      panda/src/pipeline/conditionVar.h
  22. 1 0
      panda/src/pipeline/conditionVarDummyImpl.I
  23. 1 0
      panda/src/pipeline/conditionVarDummyImpl.h
  24. 6 0
      panda/src/pipeline/conditionVarImpl.h
  25. 60 0
      panda/src/pipeline/conditionVarSimpleImpl.I
  26. 65 0
      panda/src/pipeline/conditionVarSimpleImpl.cxx
  27. 55 0
      panda/src/pipeline/conditionVarSimpleImpl.h
  28. 1 0
      panda/src/pipeline/mainThread.cxx
  29. 3 3
      panda/src/pipeline/mutexDebug.I
  30. 1 1
      panda/src/pipeline/mutexDebug.cxx
  31. 3 3
      panda/src/pipeline/mutexDebug.h
  32. 2 2
      panda/src/pipeline/mutexDirect.h
  33. 78 0
      panda/src/pipeline/mutexSimpleImpl.I
  34. 56 0
      panda/src/pipeline/mutexSimpleImpl.cxx
  35. 68 0
      panda/src/pipeline/mutexSimpleImpl.h
  36. 57 0
      panda/src/pipeline/mutexTrueImpl.h
  37. 1 0
      panda/src/pipeline/pipeline_composite1.cxx
  38. 4 0
      panda/src/pipeline/pipeline_composite2.cxx
  39. 10 5
      panda/src/pipeline/pythonThread.cxx
  40. 82 0
      panda/src/pipeline/reMutexSimpleImpl.I
  41. 57 0
      panda/src/pipeline/reMutexSimpleImpl.cxx
  42. 58 0
      panda/src/pipeline/reMutexSimpleImpl.h
  43. 4 3
      panda/src/pipeline/test_atomic.cxx
  44. 4 3
      panda/src/pipeline/test_concurrency.cxx
  45. 19 3
      panda/src/pipeline/test_delete.cxx
  46. 7 4
      panda/src/pipeline/test_diners.cxx
  47. 2 2
      panda/src/pipeline/test_mutex.cxx
  48. 44 0
      panda/src/pipeline/test_setjmp.cxx
  49. 1 1
      panda/src/pipeline/test_threaddata.cxx
  50. 45 18
      panda/src/pipeline/thread.I
  51. 5 1
      panda/src/pipeline/thread.cxx
  52. 6 2
      panda/src/pipeline/thread.h
  53. 42 5
      panda/src/pipeline/threadDummyImpl.I
  54. 4 1
      panda/src/pipeline/threadDummyImpl.h
  55. 5 0
      panda/src/pipeline/threadImpl.h
  56. 28 0
      panda/src/pipeline/threadPosixImpl.I
  57. 12 13
      panda/src/pipeline/threadPosixImpl.cxx
  58. 4 1
      panda/src/pipeline/threadPosixImpl.h
  59. 108 0
      panda/src/pipeline/threadSimpleImpl.I
  60. 273 0
      panda/src/pipeline/threadSimpleImpl.cxx
  61. 151 0
      panda/src/pipeline/threadSimpleImpl.h
  62. 54 0
      panda/src/pipeline/threadSimpleImpl_no_opt_.cxx
  63. 64 0
      panda/src/pipeline/threadSimpleManager.I
  64. 447 0
      panda/src/pipeline/threadSimpleManager.cxx
  65. 121 0
      panda/src/pipeline/threadSimpleManager.h
  66. 28 0
      panda/src/pipeline/threadWin32Impl.I
  67. 13 14
      panda/src/pipeline/threadWin32Impl.cxx
  68. 4 1
      panda/src/pipeline/threadWin32Impl.h
  69. 2 8
      panda/src/putil/copyOnWritePointer.cxx

+ 37 - 7
dtool/Config.pp

@@ -678,13 +678,39 @@
 // supports kernel threads running on different CPU's), but it will
 // supports kernel threads running on different CPU's), but it will
 // slightly slow down Panda for the single CPU case, so this is not
 // slightly slow down Panda for the single CPU case, so this is not
 // enabled by default.
 // enabled by default.
-
-// You should only turn this on if you have some threading library
-// available (you probably do).  Windows has one built-in.  Linux and
-// OSX use Posix threads.
 #define HAVE_THREADS
 #define HAVE_THREADS
 #define THREADS_LIBS $[if $[not $[WINDOWS_PLATFORM]],pthread]
 #define THREADS_LIBS $[if $[not $[WINDOWS_PLATFORM]],pthread]
 
 
+// If you have enabled threading support with HAVE_THREADS, the
+// default is to use OS-provided threading constructs, which usually
+// allows for full multiprogramming support (i.e. the program can take
+// advantage of multiple CPU's).  On the other hand, compiling in this
+// full OS-provided support can impose some runtime overhead, making
+// the application run slower on a single-CPU machine.  To avoid this
+// overhead, but still gain some of the basic functionality of threads
+// (such as support for asynchronous model loads), define
+// SIMPLE_THREADS true in addition to HAVE_THREADS.  This will compile
+// in a homespun cooperative threading implementation that runs
+// strictly on one CPU, adding very little overhead over plain
+// single-threaded code.  
+
+// Enabling SIMPLE_THREADS is highly experimental at the present time.
+// Since SIMPLE_THREADS requires special support from the C runtime
+// library, it may not be available on all platforms and
+// architectures.
+#define SIMPLE_THREADS
+
+// If you are using SIMPLE_THREADS, the default is to consider an
+// implicit context switch at every attempt to lock a mutex.  This
+// makes it less necessary to pepper your code with explicit calls to
+// Thread::consider_yield().  However, you may want to restrict this
+// behavior, and only allow context switches at explicit calls to
+// Thread::yield(), consider_yield(), and sleep() (as well as calls to
+// ConditionVar::wait()).  This gives you absolute control over when
+// the context switch happens, and makes mutexes unnecessary (mutexes
+// will be compiled out).  Define this true to build that way.
+#define SIMPLE_THREADS_NO_IMPLICIT_YIELD
+
 // Whether threading is defined or not, you might want to validate the
 // Whether threading is defined or not, you might want to validate the
 // thread and synchronization operations.  With threading enabled,
 // thread and synchronization operations.  With threading enabled,
 // defining this will also enable deadlock detection and logging.
 // defining this will also enable deadlock detection and logging.
@@ -941,6 +967,10 @@
 // (above) is set to 2, 3, or 4.
 // (above) is set to 2, 3, or 4.
 #defer OPTFLAGS -O2
 #defer OPTFLAGS -O2
 
 
+// By convention, any source file that contains the string _no_opt_ in
+// its filename won't have the above compiler optimizations run for it.
+#defer no_opt $[findstring _no_opt_,$[source]]
+
 // What define variables should be passed to the compilers for each
 // What define variables should be passed to the compilers for each
 // value of OPTIMIZE?  We separate this so we can pass these same
 // value of OPTIMIZE?  We separate this so we can pass these same
 // options to interrogate, guaranteeing that the correct interfaces
 // options to interrogate, guaranteeing that the correct interfaces
@@ -957,9 +987,9 @@
 // the output of lex and yacc) without them, but with all the other
 // the output of lex and yacc) without them, but with all the other
 // relevant flags.
 // relevant flags.
 #defer CFLAGS_OPT1 $[CDEFINES_OPT1:%=-D%] -Wall $[DEBUGFLAGS]
 #defer CFLAGS_OPT1 $[CDEFINES_OPT1:%=-D%] -Wall $[DEBUGFLAGS]
-#defer CFLAGS_OPT2 $[CDEFINES_OPT2:%=-D%] -Wall $[DEBUGFLAGS] $[OPTFLAGS]
-#defer CFLAGS_OPT3 $[CDEFINES_OPT3:%=-D%] $[DEBUGFLAGS] $[OPTFLAGS]
-#defer CFLAGS_OPT4 $[CDEFINES_OPT4:%=-D%] $[OPTFLAGS]
+#defer CFLAGS_OPT2 $[CDEFINES_OPT2:%=-D%] -Wall $[DEBUGFLAGS] $[if $[no_opt],,$[OPTFLAGS]]
+#defer CFLAGS_OPT3 $[CDEFINES_OPT3:%=-D%] $[DEBUGFLAGS] $[if $[no_opt],,$[OPTFLAGS]]
+#defer CFLAGS_OPT4 $[CDEFINES_OPT4:%=-D%] $[if $[no_opt],,$[OPTFLAGS]]
 
 
 // What additional flags should be passed to both compilers when
 // What additional flags should be passed to both compilers when
 // building shared (relocatable) sources?  Some architectures require
 // building shared (relocatable) sources?  Some architectures require

+ 13 - 0
dtool/LocalSetup.pp

@@ -143,6 +143,15 @@
 #else
 #else
 #print Configuring Panda WITHOUT Python interfaces.
 #print Configuring Panda WITHOUT Python interfaces.
 #endif
 #endif
+#if $[HAVE_THREADS]
+#if $[SIMPLE_THREADS]
+#print Compilation will include simulated threading support.
+#else
+#print Compilation will include full threading support.
+#endif
+#else
+#print Configuring Panda without threading support.
+#endif
 
 
 #print
 #print
 #print See dtool_config.h for more details about the specified configuration.
 #print See dtool_config.h for more details about the specified configuration.
@@ -266,6 +275,10 @@ $[cdefine HAVE_CHROMIUM]
 /* Define if we want to compile the threading code.  */
 /* Define if we want to compile the threading code.  */
 $[cdefine HAVE_THREADS]
 $[cdefine HAVE_THREADS]
 
 
+/* Define if we want to use fast, user-space simulated threads.  */
+$[cdefine SIMPLE_THREADS]
+$[cdefine SIMPLE_THREADS_NO_IMPLICIT_YIELD]
+
 /* Define to enable deadlock detection, mutex recursion checks, etc. */
 /* Define to enable deadlock detection, mutex recursion checks, etc. */
 $[cdefine DEBUG_THREADS]
 $[cdefine DEBUG_THREADS]
 
 

+ 1 - 1
dtool/src/dtoolbase/atomicAdjust.h

@@ -22,7 +22,7 @@
 #include "dtoolbase.h"
 #include "dtoolbase.h"
 #include "selectThreadImpl.h"
 #include "selectThreadImpl.h"
 
 
-#if defined(THREAD_DUMMY_IMPL)
+#if defined(THREAD_DUMMY_IMPL)||defined(THREAD_SIMPLE_IMPL)
 
 
 #include "atomicAdjustDummyImpl.h"
 #include "atomicAdjustDummyImpl.h"
 typedef AtomicAdjustDummyImpl AtomicAdjust;
 typedef AtomicAdjustDummyImpl AtomicAdjust;

+ 1 - 1
dtool/src/dtoolbase/mutexImpl.h

@@ -22,7 +22,7 @@
 #include "dtoolbase.h"
 #include "dtoolbase.h"
 #include "selectThreadImpl.h"
 #include "selectThreadImpl.h"
 
 
-#if defined(THREAD_DUMMY_IMPL)
+#if defined(THREAD_DUMMY_IMPL)||defined(THREAD_SIMPLE_IMPL)
 
 
 #include "mutexDummyImpl.h"
 #include "mutexDummyImpl.h"
 typedef MutexDummyImpl MutexImpl;
 typedef MutexDummyImpl MutexImpl;

+ 6 - 0
dtool/src/dtoolbase/selectThreadImpl.h

@@ -49,6 +49,12 @@
 #undef TVOLATILE
 #undef TVOLATILE
 #define TVOLATILE
 #define TVOLATILE
 
 
+#elif defined(SIMPLE_THREADS)
+// Use the simulated threading library.
+#define THREAD_SIMPLE_IMPL 1
+#undef TVOLATILE
+#define TVOLATILE
+
 #elif defined(WIN32_VC)
 #elif defined(WIN32_VC)
 
 
 // In Windows, use the native threading library.
 // In Windows, use the native threading library.

+ 12 - 0
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2276,10 +2276,14 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface
     // Treat strings as a special case.  We don't want to format the
     // Treat strings as a special case.  We don't want to format the
     // return expression. 
     // return expression. 
     if (remap->_blocking) {
     if (remap->_blocking) {
+      // With SIMPLE_THREADS, it's important that we never release the
+      // interpreter lock.
+      out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n";
       indent(out, extra_indent_level)
       indent(out, extra_indent_level)
         << "PyThreadState *_save;\n";
         << "PyThreadState *_save;\n";
       indent(out, extra_indent_level)
       indent(out, extra_indent_level)
         << "Py_UNBLOCK_THREADS\n";
         << "Py_UNBLOCK_THREADS\n";
+      out << "#endif  // HAVE_THREADS && !SIMPLE_THREADS\n";
     }
     }
     if (track_interpreter) {
     if (track_interpreter) {
       indent(out,extra_indent_level) << "in_interpreter = 0;\n";
       indent(out,extra_indent_level) << "in_interpreter = 0;\n";
@@ -2296,8 +2300,10 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface
       indent(out,extra_indent_level) << "in_interpreter = 1;\n";
       indent(out,extra_indent_level) << "in_interpreter = 1;\n";
     }
     }
     if (remap->_blocking) {
     if (remap->_blocking) {
+      out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n";
       indent(out, extra_indent_level)
       indent(out, extra_indent_level)
         << "Py_BLOCK_THREADS\n";
         << "Py_BLOCK_THREADS\n";
+      out << "#endif  // HAVE_THREADS && !SIMPLE_THREADS\n";
     }
     }
     if (!extra_cleanup.empty()) {
     if (!extra_cleanup.empty()) {
       indent(out,extra_indent_level) <<  extra_cleanup << "\n";
       indent(out,extra_indent_level) <<  extra_cleanup << "\n";
@@ -2309,10 +2315,12 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface
 
 
   } else {
   } else {
     if (remap->_blocking) {
     if (remap->_blocking) {
+      out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n";
       indent(out, extra_indent_level)
       indent(out, extra_indent_level)
         << "PyThreadState *_save;\n";
         << "PyThreadState *_save;\n";
       indent(out, extra_indent_level)
       indent(out, extra_indent_level)
         << "Py_UNBLOCK_THREADS\n";
         << "Py_UNBLOCK_THREADS\n";
+      out << "#endif  // HAVE_THREADS && !SIMPLE_THREADS\n";
     }
     }
     if (track_interpreter) {
     if (track_interpreter) {
       indent(out,extra_indent_level) << "in_interpreter = 0;\n";
       indent(out,extra_indent_level) << "in_interpreter = 0;\n";
@@ -2324,8 +2332,10 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface
         indent(out,extra_indent_level) << "in_interpreter = 1;\n";
         indent(out,extra_indent_level) << "in_interpreter = 1;\n";
       }
       }
       if (remap->_blocking) {
       if (remap->_blocking) {
+        out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n";
         indent(out, extra_indent_level)
         indent(out, extra_indent_level)
           << "Py_BLOCK_THREADS\n";
           << "Py_BLOCK_THREADS\n";
+        out << "#endif  // HAVE_THREADS && !SIMPLE_THREADS\n";
       }
       }
       if (!extra_cleanup.empty()) {
       if (!extra_cleanup.empty()) {
         indent(out,extra_indent_level) << extra_cleanup << "\n";
         indent(out,extra_indent_level) << extra_cleanup << "\n";
@@ -2344,8 +2354,10 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface
         indent(out,extra_indent_level) << "in_interpreter = 1;\n";
         indent(out,extra_indent_level) << "in_interpreter = 1;\n";
       }
       }
       if (remap->_blocking) {
       if (remap->_blocking) {
+        out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n";
         indent(out, extra_indent_level)
         indent(out, extra_indent_level)
           << "Py_BLOCK_THREADS\n";
           << "Py_BLOCK_THREADS\n";
+        out << "#endif  // HAVE_THREADS && !SIMPLE_THREADS\n";
       }
       }
       if (!extra_cleanup.empty()) {
       if (!extra_cleanup.empty()) {
         indent(out,extra_indent_level) << extra_cleanup << "\n";
         indent(out,extra_indent_level) << extra_cleanup << "\n";

+ 1 - 0
dtool/src/parser-inc/Python.h

@@ -25,5 +25,6 @@
 #define PYTHON_H
 #define PYTHON_H
 
 
 class PyObject;
 class PyObject;
+class PyThreadState;
 
 
 #endif  // PYTHON_H
 #endif  // PYTHON_H

+ 18 - 0
dtool/src/parser-inc/queue

@@ -44,4 +44,22 @@ public:
   class difference_type;
   class difference_type;
 };
 };
 
 
+template<class element>
+class priority_queue {
+public:
+  typedef element value_type;
+
+  typedef element *pointer;
+  typedef const element *const_pointer;
+  typedef element &reference;
+  typedef const element &const_reference;
+
+  class iterator;
+  class const_iterator;
+  class reverse_iterator;
+  class const_reverse_iterator;
+  typedef size_t size_type;
+  class difference_type;
+};
+
 #endif
 #endif

+ 4 - 0
dtool/src/pystub/pystub.cxx

@@ -85,6 +85,8 @@ extern "C" {
   EXPCL_DTOOLCONFIG int PyString_FromString(...);
   EXPCL_DTOOLCONFIG int PyString_FromString(...);
   EXPCL_DTOOLCONFIG int PyString_FromStringAndSize(...);
   EXPCL_DTOOLCONFIG int PyString_FromStringAndSize(...);
   EXPCL_DTOOLCONFIG int PyString_Type(...);
   EXPCL_DTOOLCONFIG int PyString_Type(...);
+  EXPCL_DTOOLCONFIG int PyThreadState_Get(...);
+  EXPCL_DTOOLCONFIG int PyThreadState_Swap(...);
   EXPCL_DTOOLCONFIG int PyTuple_GetItem(...);
   EXPCL_DTOOLCONFIG int PyTuple_GetItem(...);
   EXPCL_DTOOLCONFIG int PyTuple_New(...);
   EXPCL_DTOOLCONFIG int PyTuple_New(...);
   EXPCL_DTOOLCONFIG int PyTuple_Size(...);
   EXPCL_DTOOLCONFIG int PyTuple_Size(...);
@@ -176,6 +178,8 @@ int PyString_AsStringAndSize(...) { return 0; }
 int PyString_FromString(...) { return 0; }
 int PyString_FromString(...) { return 0; }
 int PyString_FromStringAndSize(...) { return 0; }
 int PyString_FromStringAndSize(...) { return 0; }
 int PyString_Type(...) { return 0; }
 int PyString_Type(...) { return 0; }
+int PyThreadState_Get(...) { return 0; }
+int PyThreadState_Swap(...) { return 0; }
 int PyTuple_GetItem(...) { return 0; }
 int PyTuple_GetItem(...) { return 0; }
 int PyTuple_New(...) { return 0; }
 int PyTuple_New(...) { return 0; }
 int PyTuple_Size(...) { return 0; };
 int PyTuple_Size(...) { return 0; };

+ 4 - 4
panda/src/display/graphicsEngine.cxx

@@ -506,6 +506,9 @@ remove_all_windows() {
 
 
   // And, hey, let's stop the vertex paging thread, if any.
   // And, hey, let's stop the vertex paging thread, if any.
   VertexDataPage::stop_thread();
   VertexDataPage::stop_thread();
+
+  // Well, and why not clean up all threads here?
+  Thread::prepare_for_exit();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -801,10 +804,7 @@ render_frame() {
     // Nap for a moment to yield the timeslice, to be polite to other
     // Nap for a moment to yield the timeslice, to be polite to other
     // running applications.
     // running applications.
     PStatTimer timer(_yield_pcollector, current_thread);
     PStatTimer timer(_yield_pcollector, current_thread);
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-    select(0, NULL, NULL, NULL, &tv);
+    Thread::force_yield();
   }
   }
 
 
   // Anything that happens outside of GraphicsEngine::render_frame()
   // Anything that happens outside of GraphicsEngine::render_frame()

+ 1 - 1
panda/src/event/eventHandler.cxx

@@ -31,7 +31,7 @@ EventHandler *EventHandler::_global_event_handler = 0;
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 EventHandler::
 EventHandler::
-EventHandler(EventQueue *queue) : _queue(*queue) {
+EventHandler(EventQueue *ev_queue) : _queue(*ev_queue) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/event/eventHandler.h

@@ -49,7 +49,7 @@ public:
   typedef void EventCallbackFunction(const Event *, void *);
   typedef void EventCallbackFunction(const Event *, void *);
 
 
 PUBLISHED:
 PUBLISHED:
-  EventHandler(EventQueue *queue);
+  EventHandler(EventQueue *ev_queue);
 
 
   void process_events();
   void process_events();
 
 

+ 43 - 0
panda/src/express/trueClock.I

@@ -16,6 +16,49 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+
+#ifdef WIN32_VC
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrueClock::get_short_time, Win32 implementation
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE double TrueClock::
+get_short_time() {
+  bool paranoid_clock = _paranoid_clock;
+
+  if (paranoid_clock) {
+    _lock.lock();
+  }
+
+  double time = get_short_raw_time();
+
+  if (paranoid_clock) {
+    // Check for rollforwards, rollbacks, and compensate for Speed
+    // Gear type programs by verifying against the time of day clock.
+    time = correct_time(time);
+    _lock.release();
+  }
+
+  return time;
+}
+
+#else  // WIN32_VC
+
+////////////////////////////////////////////////////////////////////
+//     Function: TrueClock::get_short_time, Posix implementation
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE double TrueClock::
+get_short_time() {
+  return get_short_raw_time();
+}
+
+#endif  // WIN32_VC
+
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TrueClock::get_error_count
 //     Function: TrueClock::get_error_count
 //       Access: Published
 //       Access: Published

+ 4 - 17
panda/src/express/trueClock.cxx

@@ -86,18 +86,14 @@ get_long_time() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: TrueClock::get_short_time, Win32 implementation
+//     Function: TrueClock::get_short_raw_time, Win32 implementation
 //       Access: Published
 //       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 double TrueClock::
 double TrueClock::
-get_short_time() {
+get_short_raw_time() {
   double time;
   double time;
 
 
-  if (_paranoid_clock) {
-    _lock.lock();
-  }
-
   if (_has_high_res) {
   if (_has_high_res) {
     // Use the high-resolution clock.  This is of questionable value,
     // Use the high-resolution clock.  This is of questionable value,
     // since (a) on some OS's and hardware, the low 24 bits can
     // since (a) on some OS's and hardware, the low 24 bits can
@@ -122,15 +118,6 @@ get_short_time() {
     int tc = GetTickCount();
     int tc = GetTickCount();
     time = (double)(tc - _init_tc) * _0001;
     time = (double)(tc - _init_tc) * _0001;
   }
   }
-
-  if (_paranoid_clock) {
-    // Check for rollforwards, rollbacks, and compensate for Speed
-    // Gear type programs by verifying against the time of day clock.
-    time = correct_time(time);
-    _lock.release();
-  }
-
-  return time;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -545,12 +532,12 @@ get_long_time() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: TrueClock::get_short_time, Posix implementation
+//     Function: TrueClock::get_short_raw_time, Posix implementation
 //       Access: Published
 //       Access: Published
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 double TrueClock::
 double TrueClock::
-get_short_time() {
+get_short_raw_time() {
   struct timeval tv;
   struct timeval tv;
 
 
   int result;
   int result;

+ 6 - 1
panda/src/express/trueClock.h

@@ -50,7 +50,12 @@ PUBLISHED:
   // short interval.  It may tend to drift over the long haul, but it
   // short interval.  It may tend to drift over the long haul, but it
   // should have lots of digits to measure short intervals very
   // should have lots of digits to measure short intervals very
   // precisely.
   // precisely.
-  double get_short_time();
+  INLINE double get_short_time();
+
+  // get_short_raw_time() is like get_short_time(), but does not apply
+  // any corrections (e.g. paranoid-clock) to the result returned by
+  // the OS.
+  double get_short_raw_time();
 
 
   INLINE int get_error_count() const;
   INLINE int get_error_count() const;
 
 

+ 2 - 0
panda/src/framework/pandaFramework.cxx

@@ -146,6 +146,8 @@ close_framework() {
   _exit_flag = false;
   _exit_flag = false;
 
 
   _recorder = NULL;
   _recorder = NULL;
+
+  Thread::prepare_for_exit();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 0
panda/src/gobj/vertexDataPage.cxx

@@ -744,6 +744,8 @@ thread_main() {
 
 
     _working_page = NULL;
     _working_page = NULL;
     _working_cvar.signal();
     _working_cvar.signal();
+
+    Thread::consider_yield();
   }
   }
 }
 }
 
 

+ 58 - 9
panda/src/pipeline/Sources.pp

@@ -11,6 +11,8 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
 
 
   #define SOURCES \
   #define SOURCES \
+    ThreadSimpleImpl_no_opt_.cxx \
+    blockerSimple.h blockerSimple.I \
     conditionVar.h conditionVar.I \
     conditionVar.h conditionVar.I \
     conditionVarDebug.h conditionVarDebug.I \
     conditionVarDebug.h conditionVarDebug.I \
     conditionVarDirect.h conditionVarDirect.I \
     conditionVarDirect.h conditionVarDirect.I \
@@ -22,6 +24,7 @@
     conditionVarImpl.h \
     conditionVarImpl.h \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
+    conditionVarSimpleImpl.h conditionVarSimpleImpl.I \
     conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \
     conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \
     config_pipeline.h \
     config_pipeline.h \
     cycleData.h cycleData.I \
     cycleData.h cycleData.I \
@@ -37,6 +40,8 @@
     mutexDebug.h mutexDebug.I \
     mutexDebug.h mutexDebug.I \
     mutexDirect.h mutexDirect.I \
     mutexDirect.h mutexDirect.I \
     mutexHolder.h mutexHolder.I \
     mutexHolder.h mutexHolder.I \
+    mutexSimpleImpl.h mutexSimpleImpl.I \
+    mutexTrueImpl.h \
     pipeline.h pipeline.I \
     pipeline.h pipeline.I \
     pipelineCycler.h pipelineCycler.I \
     pipelineCycler.h pipelineCycler.I \
     pipelineCyclerLinks.h pipelineCyclerLinks.I \
     pipelineCyclerLinks.h pipelineCyclerLinks.I \
@@ -49,8 +54,12 @@
     reMutex.I reMutex.h \
     reMutex.I reMutex.h \
     reMutexDirect.h reMutexDirect.I \
     reMutexDirect.h reMutexDirect.I \
     reMutexHolder.I reMutexHolder.h \
     reMutexHolder.I reMutexHolder.h \
-    threadDummyImpl.h threadDummyImpl.I thread.h thread.I threadImpl.h \
+    reMutexSimpleImpl.h reMutexSimpleImpl.I \
+    thread.h thread.I threadImpl.h \
+    threadDummyImpl.h threadDummyImpl.I \
     threadPosixImpl.h threadPosixImpl.I \
     threadPosixImpl.h threadPosixImpl.I \
+    threadSimpleImpl.h threadSimpleImpl.I  \
+    threadSimpleManager.h threadSimpleManager.I  \
     threadWin32Impl.h threadWin32Impl.I \
     threadWin32Impl.h threadWin32Impl.I \
     threadPriority.h
     threadPriority.h
 
 
@@ -65,6 +74,7 @@
     conditionVarFullWin32Impl.cxx \
     conditionVarFullWin32Impl.cxx \
     conditionVarPosixImpl.cxx \
     conditionVarPosixImpl.cxx \
     conditionVarWin32Impl.cxx \
     conditionVarWin32Impl.cxx \
+    conditionVarSimpleImpl.cxx \
     conditionVarSpinlockImpl.cxx \
     conditionVarSpinlockImpl.cxx \
     config_pipeline.cxx \
     config_pipeline.cxx \
     cycleData.cxx \
     cycleData.cxx \
@@ -80,6 +90,7 @@
     mutexDebug.cxx \
     mutexDebug.cxx \
     mutexDirect.cxx \
     mutexDirect.cxx \
     mutexHolder.cxx \
     mutexHolder.cxx \
+    mutexSimpleImpl.cxx \
     pipeline.cxx \
     pipeline.cxx \
     pipelineCycler.cxx \
     pipelineCycler.cxx \
     pipelineCyclerDummyImpl.cxx \
     pipelineCyclerDummyImpl.cxx \
@@ -90,11 +101,16 @@
     reMutex.cxx \
     reMutex.cxx \
     reMutexDirect.cxx \
     reMutexDirect.cxx \
     reMutexHolder.cxx \
     reMutexHolder.cxx \
-    thread.cxx threadDummyImpl.cxx \
+    reMutexSimpleImpl.cxx \
+    thread.cxx \
+    threadDummyImpl.cxx \
     threadPosixImpl.cxx \
     threadPosixImpl.cxx \
+    threadSimpleImpl.cxx \
+    threadSimpleManager.cxx \
     threadWin32Impl.cxx
     threadWin32Impl.cxx
 
 
   #define INSTALL_HEADERS  \
   #define INSTALL_HEADERS  \
+    blockerSimple.h blockerSimple.I \
     conditionVar.h conditionVar.I \
     conditionVar.h conditionVar.I \
     conditionVarDebug.h conditionVarDebug.I \
     conditionVarDebug.h conditionVarDebug.I \
     conditionVarDirect.h conditionVarDirect.I \
     conditionVarDirect.h conditionVarDirect.I \
@@ -106,6 +122,7 @@
     conditionVarImpl.h \
     conditionVarImpl.h \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
+    conditionVarSimpleImpl.h conditionVarSimpleImpl.I \
     conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \
     conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \
     config_pipeline.h \
     config_pipeline.h \
     cycleData.h cycleData.I \
     cycleData.h cycleData.I \
@@ -121,6 +138,8 @@
     mutexDebug.h mutexDebug.I \
     mutexDebug.h mutexDebug.I \
     mutexDirect.h mutexDirect.I \
     mutexDirect.h mutexDirect.I \
     mutexHolder.h mutexHolder.I \
     mutexHolder.h mutexHolder.I \
+    mutexSimpleImpl.h mutexSimpleImpl.I \
+    mutexTrueImpl.h \
     pipeline.h pipeline.I \
     pipeline.h pipeline.I \
     pipelineCycler.h pipelineCycler.I \
     pipelineCycler.h pipelineCycler.I \
     pipelineCyclerLinks.h pipelineCyclerLinks.I \
     pipelineCyclerLinks.h pipelineCyclerLinks.I \
@@ -133,8 +152,12 @@
     reMutex.I reMutex.h \
     reMutex.I reMutex.h \
     reMutexDirect.h reMutexDirect.I \
     reMutexDirect.h reMutexDirect.I \
     reMutexHolder.I reMutexHolder.h \
     reMutexHolder.I reMutexHolder.h \
-    threadDummyImpl.h threadDummyImpl.I thread.h thread.I threadImpl.h \
+    reMutexSimpleImpl.h reMutexSimpleImpl.I \
+    thread.h thread.I threadImpl.h \
+    threadDummyImpl.h threadDummyImpl.I \
     threadPosixImpl.h threadPosixImpl.I \
     threadPosixImpl.h threadPosixImpl.I \
+    threadSimpleImpl.h threadSimpleImpl.I \
+    threadSimpleManager.h threadSimpleManager.I \
     threadWin32Impl.h threadWin32Impl.I \
     threadWin32Impl.h threadWin32Impl.I \
     threadPriority.h
     threadPriority.h
 
 
@@ -146,7 +169,9 @@
 #begin test_bin_target
 #begin test_bin_target
   #define TARGET test_threaddata
   #define TARGET test_threaddata
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
-  #define OTHER_LIBS dtoolutil:c dtool:m pystub
+  #define OTHER_LIBS \
+   interrogatedb:c dconfig:c dtoolbase:c prc:c \
+   dtoolutil:c dtool:m pystub
 
 
   #define SOURCES \
   #define SOURCES \
     test_threaddata.cxx
     test_threaddata.cxx
@@ -157,7 +182,9 @@
 #begin test_bin_target
 #begin test_bin_target
   #define TARGET test_diners
   #define TARGET test_diners
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
-  #define OTHER_LIBS dtoolutil:c dtool:m dtoolconfig:m pystub
+  #define OTHER_LIBS \
+   interrogatedb:c dconfig:c dtoolbase:c prc:c \
+   dtoolutil:c dtool:m pystub
 
 
   #define SOURCES \
   #define SOURCES \
     test_diners.cxx
     test_diners.cxx
@@ -168,7 +195,9 @@
 #begin test_bin_target
 #begin test_bin_target
   #define TARGET test_mutex
   #define TARGET test_mutex
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
-  #define OTHER_LIBS dtoolutil:c dtool:m dtoolconfig:m pystub
+  #define OTHER_LIBS \
+   interrogatedb:c dconfig:c dtoolbase:c prc:c \
+   dtoolutil:c dtool:m pystub
 
 
   #define SOURCES \
   #define SOURCES \
     test_mutex.cxx
     test_mutex.cxx
@@ -179,7 +208,9 @@
 #begin test_bin_target
 #begin test_bin_target
   #define TARGET test_concurrency
   #define TARGET test_concurrency
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
-  #define OTHER_LIBS dtoolutil:c dtool:m dtoolconfig:m pystub
+  #define OTHER_LIBS \
+   interrogatedb:c dconfig:c dtoolbase:c prc:c \
+   dtoolutil:c dtool:m pystub
 
 
   #define SOURCES \
   #define SOURCES \
     test_concurrency.cxx
     test_concurrency.cxx
@@ -190,7 +221,9 @@
 #begin test_bin_target
 #begin test_bin_target
   #define TARGET test_delete
   #define TARGET test_delete
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
-  #define OTHER_LIBS dtoolutil:c dtool:m dtoolconfig:m pystub
+  #define OTHER_LIBS \
+   interrogatedb:c dconfig:c dtoolbase:c prc:c \
+   dtoolutil:c dtool:m pystub
 
 
   #define SOURCES \
   #define SOURCES \
     test_delete.cxx
     test_delete.cxx
@@ -201,10 +234,26 @@
 #begin test_bin_target
 #begin test_bin_target
   #define TARGET test_atomic
   #define TARGET test_atomic
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
   #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
-  #define OTHER_LIBS dtoolutil:c dtool:m dtoolconfig:m pystub
+  #define OTHER_LIBS \
+   interrogatedb:c dconfig:c dtoolbase:c prc:c \
+   dtoolutil:c dtool:m pystub
 
 
   #define SOURCES \
   #define SOURCES \
     test_atomic.cxx
     test_atomic.cxx
 
 
 #end test_bin_target
 #end test_bin_target
 
 
+
+
+#begin test_bin_target
+  #define TARGET test_setjmp
+  #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
+  #define OTHER_LIBS \
+   interrogatedb:c dconfig:c dtoolbase:c prc:c \
+   dtoolutil:c dtool:m pystub
+
+  #define SOURCES \
+    test_setjmp.cxx
+
+#end test_bin_target
+

+ 38 - 0
panda/src/pipeline/blockerSimple.I

@@ -0,0 +1,38 @@
+// Filename: blockerSimple.I
+// Created by:  drose (20Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: BlockerSimple::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE BlockerSimple::
+BlockerSimple() {
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BlockerSimple::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE BlockerSimple::
+~BlockerSimple() {
+  nassertv(_flags == 0);
+}

+ 57 - 0
panda/src/pipeline/blockerSimple.h

@@ -0,0 +1,57 @@
+// Filename: blockerSimple.h
+// Created by:  drose (20Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 BLOCKERSIMPLE_H
+#define BLOCKERSIMPLE_H
+
+#include "pandabase.h"
+#include "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "pnotify.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : BlockerSimple
+// Description : This is a base class for MutexSimpleImpl and
+//               ConditionVarSimpleImpl.  It represents a
+//               synchronization primitive that one or more threads
+//               might be blocked on.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA BlockerSimple {
+protected:
+  INLINE BlockerSimple();
+  INLINE ~BlockerSimple();
+
+protected:
+  enum Flags {
+    // lock_count is only used for mutexes, not condition variables.
+    F_lock_count   = 0x3fffffff,
+    F_has_waiters  = 0x40000000,
+  };
+
+  unsigned int _flags;
+
+  friend class ThreadSimpleManager;
+};
+
+#include "blockerSimple.I"
+
+#endif  // THREAD_SIMPLE_IMPL
+
+#endif

+ 1 - 1
panda/src/pipeline/conditionVar.h

@@ -30,7 +30,7 @@
 //               waiting for something to happen.  A condition
 //               waiting for something to happen.  A condition
 //               variable can be used to "wake up" a thread when some
 //               variable can be used to "wake up" a thread when some
 //               arbitrary condition has changed.
 //               arbitrary condition has changed.
-
+//
 //               The ConditionVar class does not support the full
 //               The ConditionVar class does not support the full
 //               semantics of POSIX condition variables.  In
 //               semantics of POSIX condition variables.  In
 //               particular, it does not support the broadcast or
 //               particular, it does not support the broadcast or

+ 1 - 0
panda/src/pipeline/conditionVarDummyImpl.I

@@ -42,6 +42,7 @@ INLINE ConditionVarDummyImpl::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void ConditionVarDummyImpl::
 INLINE void ConditionVarDummyImpl::
 wait() {
 wait() {
+  Thread::force_yield();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/pipeline/conditionVarDummyImpl.h

@@ -21,6 +21,7 @@
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 #include "selectThreadImpl.h"
 #include "selectThreadImpl.h"
+#include "thread.h"
 
 
 #include "pnotify.h"
 #include "pnotify.h"
 
 

+ 6 - 0
panda/src/pipeline/conditionVarImpl.h

@@ -28,6 +28,12 @@
 typedef ConditionVarDummyImpl ConditionVarImpl;
 typedef ConditionVarDummyImpl ConditionVarImpl;
 typedef ConditionVarDummyImpl ConditionVarFullImpl;
 typedef ConditionVarDummyImpl ConditionVarFullImpl;
 
 
+#elif defined(THREAD_SIMPLE_IMPL)
+
+#include "conditionVarSimpleImpl.h"
+typedef ConditionVarSimpleImpl ConditionVarImpl;
+typedef ConditionVarSimpleImpl ConditionVarFullImpl;
+
 #elif defined(MUTEX_SPINLOCK)
 #elif defined(MUTEX_SPINLOCK)
 
 
 #include "conditionVarSpinlockImpl.h"
 #include "conditionVarSpinlockImpl.h"

+ 60 - 0
panda/src/pipeline/conditionVarSimpleImpl.I

@@ -0,0 +1,60 @@
+// Filename: conditionVarSimpleImpl.I
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: ConditionVarSimpleImpl::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ConditionVarSimpleImpl::
+ConditionVarSimpleImpl(MutexTrueImpl &mutex) : _mutex(mutex) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarSimpleImpl::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ConditionVarSimpleImpl::
+~ConditionVarSimpleImpl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarSimpleImpl::signal
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ConditionVarSimpleImpl::
+signal() {
+  if (_flags & F_has_waiters) {
+    do_signal();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarSimpleImpl::signal_all
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ConditionVarSimpleImpl::
+signal_all() {
+  if (_flags & F_has_waiters) {
+    do_signal_all();
+  }
+}

+ 65 - 0
panda/src/pipeline/conditionVarSimpleImpl.cxx

@@ -0,0 +1,65 @@
+// Filename: conditionVarSimpleImpl.cxx
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "conditionVarSimpleImpl.h"
+#include "threadSimpleImpl.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarSimpleImpl::wait
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConditionVarSimpleImpl::
+wait() {
+  _mutex.release();
+
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  ThreadSimpleImpl *thread = manager->get_current_thread();
+  manager->enqueue_block(thread, this);
+  manager->next_context();
+
+  _mutex.lock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarSimpleImpl::do_signal
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConditionVarSimpleImpl::
+do_signal() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  manager->unblock_one(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarSimpleImpl::do_signal_all
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConditionVarSimpleImpl::
+do_signal_all() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  manager->unblock_all(this);
+}
+
+#endif  // THREAD_SIMPLE_IMPL

+ 55 - 0
panda/src/pipeline/conditionVarSimpleImpl.h

@@ -0,0 +1,55 @@
+// Filename: conditionVarSimpleImpl.h
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CONDITIONVARSIMPLEIMPL_H
+#define CONDITIONVARSIMPLEIMPL_H
+
+#include "pandabase.h"
+#include "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "blockerSimple.h"
+#include "mutexTrueImpl.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConditionVarSimpleImpl
+// Description : Implements a simple condition variable using
+//               simulated user-space threads.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ConditionVarSimpleImpl : public BlockerSimple {
+public:
+  INLINE ConditionVarSimpleImpl(MutexTrueImpl &mutex);
+  INLINE ~ConditionVarSimpleImpl();
+
+  void wait();
+  INLINE void signal();
+  INLINE void signal_all();
+
+private:
+  void do_signal();
+  void do_signal_all();
+
+  MutexTrueImpl &_mutex;
+};
+
+#include "conditionVarSimpleImpl.I"
+
+#endif  // THREAD_SIMPLE_IMPL
+
+#endif

+ 1 - 0
panda/src/pipeline/mainThread.cxx

@@ -28,6 +28,7 @@ TypeHandle MainThread::_type_handle;
 MainThread::
 MainThread::
 MainThread() : Thread("Main", "Main") {
 MainThread() : Thread("Main", "Main") {
   init_type();  // in case static init comes in the wrong order
   init_type();  // in case static init comes in the wrong order
+  _impl.setup_main_thread();
   _started = true;
   _started = true;
 }
 }
  
  

+ 3 - 3
panda/src/pipeline/mutexDebug.I

@@ -151,10 +151,10 @@ debug_is_locked() const {
 //               (non-static) methods of MutexDebug may simply assume
 //               (non-static) methods of MutexDebug may simply assume
 //               that the pointer has already been created.
 //               that the pointer has already been created.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE MutexImpl *MutexDebug::
+INLINE MutexTrueImpl *MutexDebug::
 get_global_lock() {
 get_global_lock() {
-  if (_global_lock == (MutexImpl *)NULL) {
-    _global_lock = new MutexImpl;
+  if (_global_lock == (MutexTrueImpl *)NULL) {
+    _global_lock = new MutexTrueImpl;
   }
   }
   return _global_lock;
   return _global_lock;
 }
 }

+ 1 - 1
panda/src/pipeline/mutexDebug.cxx

@@ -22,7 +22,7 @@
 
 
 #ifdef DEBUG_THREADS
 #ifdef DEBUG_THREADS
 
 
-MutexImpl *MutexDebug::_global_lock;
+MutexTrueImpl *MutexDebug::_global_lock;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDebug::Constructor
 //     Function: MutexDebug::Constructor

+ 3 - 3
panda/src/pipeline/mutexDebug.h

@@ -20,7 +20,7 @@
 #define MUTEXDEBUG_H
 #define MUTEXDEBUG_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "mutexImpl.h"
+#include "mutexTrueImpl.h"
 #include "conditionVarImpl.h"
 #include "conditionVarImpl.h"
 #include "thread.h"
 #include "thread.h"
 
 
@@ -59,7 +59,7 @@ private:
   void report_deadlock(Thread *this_thread);
   void report_deadlock(Thread *this_thread);
 
 
 private:
 private:
-  INLINE static MutexImpl *get_global_lock();
+  INLINE static MutexTrueImpl *get_global_lock();
 
 
   string _name;
   string _name;
   bool _allow_recursion;
   bool _allow_recursion;
@@ -68,7 +68,7 @@ private:
 
 
   ConditionVarImpl _cvar_impl;
   ConditionVarImpl _cvar_impl;
 
 
-  static MutexImpl *_global_lock;
+  static MutexTrueImpl *_global_lock;
 
 
   friend class ConditionVarDebug;
   friend class ConditionVarDebug;
   friend class ConditionVarFullDebug;
   friend class ConditionVarFullDebug;

+ 2 - 2
panda/src/pipeline/mutexDirect.h

@@ -20,7 +20,7 @@
 #define MUTEXDIRECT_H
 #define MUTEXDIRECT_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "mutexImpl.h"
+#include "mutexTrueImpl.h"
 #include "pnotify.h"
 #include "pnotify.h"
 
 
 class Thread;
 class Thread;
@@ -49,7 +49,7 @@ PUBLISHED:
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 
 private:
 private:
-  MutexImpl _impl;
+  MutexTrueImpl _impl;
 
 
   friend class ConditionVarDirect;
   friend class ConditionVarDirect;
   friend class ConditionVarFullDirect;
   friend class ConditionVarFullDirect;

+ 78 - 0
panda/src/pipeline/mutexSimpleImpl.I

@@ -0,0 +1,78 @@
+// Filename: mutexSimpleImpl.I
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: MutexSimpleImpl::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE MutexSimpleImpl::
+MutexSimpleImpl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexSimpleImpl::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE MutexSimpleImpl::
+~MutexSimpleImpl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexSimpleImpl::lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void MutexSimpleImpl::
+lock() {
+  if (!try_lock()) {
+    do_lock();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexSimpleImpl::try_lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool MutexSimpleImpl::
+try_lock() {
+  ThreadSimpleImpl::consider_yield();
+  if ((_flags & F_lock_count) != 0) {
+    return false;
+  }
+  _flags |= F_lock_count;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexSimpleImpl::release
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void MutexSimpleImpl::
+release() {
+  nassertv((_flags & F_lock_count) != 0);
+  _flags &= ~F_lock_count;
+
+  if (_flags & F_has_waiters) {
+    do_release();
+  }
+}

+ 56 - 0
panda/src/pipeline/mutexSimpleImpl.cxx

@@ -0,0 +1,56 @@
+// Filename: mutexSimpleImpl.cxx
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "mutexSimpleImpl.h"
+#include "threadSimpleImpl.h"
+#include "threadSimpleManager.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexSimpleImpl::do_lock
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void MutexSimpleImpl::
+do_lock() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  ThreadSimpleImpl *thread = manager->get_current_thread();
+
+  while ((_flags & F_lock_count) != 0) {
+    manager->enqueue_block(thread, this);
+    manager->next_context();
+  }
+  
+  _flags |= F_lock_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexSimpleImpl::do_release
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void MutexSimpleImpl::
+do_release() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  manager->unblock_one(this);
+}
+
+#endif  // THREAD_SIMPLE_IMPL

+ 68 - 0
panda/src/pipeline/mutexSimpleImpl.h

@@ -0,0 +1,68 @@
+// Filename: mutexSimpleImpl.h
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 MUTEXSIMPLEIMPL_H
+#define MUTEXSIMPLEIMPL_H
+
+#include "pandabase.h"
+#include "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "blockerSimple.h"
+#include "threadSimpleImpl.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : MutexSimpleImpl
+// Description : This is the mutex implementation for the simple,
+//               simulated threads case.  It's designed to be as
+//               lightweight as possible, of course.  This
+//               implementation simply yields the thread when the
+//               mutex would block.
+//
+//               We can't define this class in dtoolbase along with
+//               the other mutex implementations, because this
+//               implementation requires knowing about the
+//               SimpleThreadManager.  This complicates the
+//               MutexDirect and MutexDebug definitions (we have to
+//               define a MutexImpl, for code before pipeline to
+//               use--which maps to MutexDummyImpl--and a
+//               MutexTrueImpl, for code after pipeline to use--which
+//               maps to this class, MutexSimpleImpl).
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA MutexSimpleImpl : public BlockerSimple {
+public:
+  INLINE MutexSimpleImpl();
+  INLINE ~MutexSimpleImpl();
+
+  INLINE void lock();
+  INLINE bool try_lock();
+  INLINE void release();
+
+private:
+  void do_lock();
+  void do_release();
+
+  friend class ThreadSimpleManager;
+};
+
+#include "mutexSimpleImpl.I"
+
+#endif  // THREAD_SIMPLE_IMPL
+
+#endif

+ 57 - 0
panda/src/pipeline/mutexTrueImpl.h

@@ -0,0 +1,57 @@
+// Filename: mutexTrueImpl.h
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 MUTEXTRUEIMPL_H
+#define MUTEXTRUEIMPL_H
+
+#include "pandabase.h"
+#include "mutexImpl.h"
+
+// The MutexTrueImpl typedef is given here in the pipeline directory,
+// and is used to implement Mutex and ReMutex (and, therefore, any
+// downstream Mutex implementation).
+
+// This is slightly different from the MutexImpl typedef, which is
+// given up in dtoolbase, and is used standalone anywhere very
+// low-level code needs to protect itself from mutual exclusion.
+
+// The only difference between the two is in the case of
+// THREAD_SIMPLE_IMPL.  In this case, MutexImpl maps to
+// MutexDummyImpl, while MutexTrueImpl maps to MutexSimpleImpl.  This
+// distinction is necessary because we cannot define MutexSimpleImpl
+// until we have defined the whole ThreadSimpleManager and related
+// infrastructure.
+
+#if defined(THREAD_SIMPLE_IMPL) && !defined(SIMPLE_THREADS_NO_IMPLICIT_YIELD)
+
+#include "mutexSimpleImpl.h"
+#include "reMutexSimpleImpl.h"
+typedef MutexSimpleImpl MutexTrueImpl;
+typedef ReMutexSimpleImpl ReMutexTrueImpl;
+
+#else
+
+typedef MutexImpl MutexTrueImpl;
+typedef ReMutexImpl ReMutexTrueImpl;
+
+#endif
+
+#endif
+
+
+

+ 1 - 0
panda/src/pipeline/pipeline_composite1.cxx

@@ -7,6 +7,7 @@
 #include "conditionVarFullDirect.cxx"
 #include "conditionVarFullDirect.cxx"
 #include "conditionVarPosixImpl.cxx"
 #include "conditionVarPosixImpl.cxx"
 #include "conditionVarWin32Impl.cxx"
 #include "conditionVarWin32Impl.cxx"
+#include "conditionVarSimpleImpl.cxx"
 #include "conditionVarSpinlockImpl.cxx"
 #include "conditionVarSpinlockImpl.cxx"
 #include "config_pipeline.cxx"
 #include "config_pipeline.cxx"
 #include "cycleData.cxx"
 #include "cycleData.cxx"

+ 4 - 0
panda/src/pipeline/pipeline_composite2.cxx

@@ -3,6 +3,7 @@
 #include "mutexDebug.cxx"
 #include "mutexDebug.cxx"
 #include "mutexDirect.cxx"
 #include "mutexDirect.cxx"
 #include "mutexHolder.cxx"
 #include "mutexHolder.cxx"
+#include "mutexSimpleImpl.cxx"
 #include "pipeline.cxx"
 #include "pipeline.cxx"
 #include "pipelineCycler.cxx"
 #include "pipelineCycler.cxx"
 #include "pipelineCyclerDummyImpl.cxx"
 #include "pipelineCyclerDummyImpl.cxx"
@@ -13,7 +14,10 @@
 #include "reMutex.cxx"
 #include "reMutex.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexHolder.cxx"
 #include "reMutexHolder.cxx"
+#include "reMutexSimpleImpl.cxx"
 #include "thread.cxx"
 #include "thread.cxx"
 #include "threadDummyImpl.cxx"
 #include "threadDummyImpl.cxx"
 #include "threadPosixImpl.cxx"
 #include "threadPosixImpl.cxx"
+#include "threadSimpleImpl.cxx"
+#include "threadSimpleManager.cxx"
 #include "threadWin32Impl.cxx"
 #include "threadWin32Impl.cxx"

+ 10 - 5
panda/src/pipeline/pythonThread.cxx

@@ -132,16 +132,16 @@ handle_python_exception() {
   ostringstream strm;
   ostringstream strm;
   strm << "\n";
   strm << "\n";
 
 
-  PyObject *exc_name = PyObject_GetAttrString(exc, "__name__");
-  if (exc_name == (PyObject *)NULL) {
-    PyObject *exc_str = PyObject_Str(exc);
+  if (PyObject_HasAttrString(exc, "__name__")) {
+    PyObject *exc_name = PyObject_GetAttrString(exc, "__name__");
+    PyObject *exc_str = PyObject_Str(exc_name);
     strm << PyString_AsString(exc_str);
     strm << PyString_AsString(exc_str);
     Py_DECREF(exc_str);
     Py_DECREF(exc_str);
+    Py_DECREF(exc_name);
   } else {
   } else {
-    PyObject *exc_str = PyObject_Str(exc_name);
+    PyObject *exc_str = PyObject_Str(exc);
     strm << PyString_AsString(exc_str);
     strm << PyString_AsString(exc_str);
     Py_DECREF(exc_str);
     Py_DECREF(exc_str);
-    Py_DECREF(exc_name);
   }
   }
   Py_DECREF(exc);
   Py_DECREF(exc);
 
 
@@ -160,6 +160,11 @@ handle_python_exception() {
   nout << message << "\n";
   nout << message << "\n";
 
 
   nassert_raise(message);
   nassert_raise(message);
+
+  // Now attempt to force the main thread to the head of the ready
+  // queue, so it will be the one to receive the above assertion.
+  // This mainly only has an effect if SIMPLE_THREADS is in use.
+  Thread::get_main_thread()->preempt();
 }
 }
 
 
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON

+ 82 - 0
panda/src/pipeline/reMutexSimpleImpl.I

@@ -0,0 +1,82 @@
+// Filename: reMutexSimpleImpl.I
+// Created by:  drose (20Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: ReMutexSimpleImpl::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexSimpleImpl::
+ReMutexSimpleImpl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexSimpleImpl::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexSimpleImpl::
+~ReMutexSimpleImpl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexSimpleImpl::lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexSimpleImpl::
+lock() {
+  if (!try_lock()) {
+    do_lock();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexSimpleImpl::try_lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool ReMutexSimpleImpl::
+try_lock() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  ThreadSimpleImpl *thread = manager->get_current_thread();
+
+  thread->consider_yield_this();
+  if ((_flags & F_lock_count) != 0) {
+    return false;
+  }
+  ++_flags;
+  _locking_thread = thread;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexSimpleImpl::release
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexSimpleImpl::
+release() {
+  nassertv((_flags & F_lock_count) != 0);
+
+  --_flags;
+  if ((_flags & F_lock_count) == 0 && (_flags & F_has_waiters)) {
+    do_release();
+  }
+}

+ 57 - 0
panda/src/pipeline/reMutexSimpleImpl.cxx

@@ -0,0 +1,57 @@
+// Filename: reMutexSimpleImpl.cxx
+// Created by:  drose (20Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "reMutexSimpleImpl.h"
+#include "threadSimpleImpl.h"
+#include "threadSimpleManager.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexSimpleImpl::do_lock
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ReMutexSimpleImpl::
+do_lock() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  ThreadSimpleImpl *thread = manager->get_current_thread();
+
+  while ((_flags & F_lock_count) != 0) {
+    manager->enqueue_block(thread, this);
+    manager->next_context();
+  }
+  
+  ++_flags;
+  _locking_thread = thread;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexSimpleImpl::do_release
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ReMutexSimpleImpl::
+do_release() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  manager->unblock_one(this);
+}
+
+#endif  // THREAD_SIMPLE_IMPL

+ 58 - 0
panda/src/pipeline/reMutexSimpleImpl.h

@@ -0,0 +1,58 @@
+// Filename: reMutexSimpleImpl.h
+// Created by:  drose (20Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 REMUTEXSIMPLEIMPL_H
+#define REMUTEXSIMPLEIMPL_H
+
+#include "pandabase.h"
+#include "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "blockerSimple.h"
+#include "threadSimpleImpl.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ReMutexSimpleImpl
+// Description : This is the mutex implementation for the simple,
+//               simulated threads case, for recursive mutexes.  See
+//               MutexSimpleImple.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ReMutexSimpleImpl : public BlockerSimple {
+public:
+  INLINE ReMutexSimpleImpl();
+  INLINE ~ReMutexSimpleImpl();
+
+  INLINE void lock();
+  INLINE bool try_lock();
+  INLINE void release();
+
+private:
+  void do_lock();
+  void do_release();
+
+  ThreadSimpleImpl *_locking_thread;
+
+  friend class ThreadSimpleManager;
+};
+
+#include "reMutexSimpleImpl.I"
+
+#endif  // THREAD_SIMPLE_IMPL
+
+#endif

+ 4 - 3
panda/src/pipeline/test_atomic.cxx

@@ -73,13 +73,13 @@ main(int argc, char *argv[]) {
 
 
   PT(MyThread) thread = new MyThread("a");
   PT(MyThread) thread = new MyThread("a");
   threads.push_back(thread);
   threads.push_back(thread);
-  thread->start(TP_normal, true, true);
+  thread->start(TP_normal, true);
 
 
   for (int i = 1; i < number_of_threads; ++i) {
   for (int i = 1; i < number_of_threads; ++i) {
     char name = 'a' + i;
     char name = 'a' + i;
     PT(MyThread) thread = new MyThread(string(1, name));
     PT(MyThread) thread = new MyThread(string(1, name));
     threads.push_back(thread);
     threads.push_back(thread);
-    thread->start(TP_normal, true, true);
+    thread->start(TP_normal, true);
   }
   }
 
 
   // Now join all the threads.
   // Now join all the threads.
@@ -93,5 +93,6 @@ main(int argc, char *argv[]) {
        << "net_count = " << _net_count << "\n"
        << "net_count = " << _net_count << "\n"
        << "num_net_count_incremented = " << _num_net_count_incremented << "\n";
        << "num_net_count_incremented = " << _num_net_count_incremented << "\n";
 
 
-  exit(0);
+  Thread::prepare_for_exit();
+  return (0);
 }
 }

+ 4 - 3
panda/src/pipeline/test_concurrency.cxx

@@ -101,14 +101,14 @@ main(int argc, char *argv[]) {
 
 
   PT(MyThread) thread = new MyThread("a", 0);
   PT(MyThread) thread = new MyThread("a", 0);
   threads.push_back(thread);
   threads.push_back(thread);
-  thread->start(TP_normal, true, true);
+  thread->start(TP_normal, true);
 
 
   for (int i = 1; i < number_of_threads; ++i) {
   for (int i = 1; i < number_of_threads; ++i) {
     char name = 'a' + i;
     char name = 'a' + i;
     Thread::sleep(delay_between_threads);
     Thread::sleep(delay_between_threads);
     PT(MyThread) thread = new MyThread(string(1, name), i);
     PT(MyThread) thread = new MyThread(string(1, name), i);
     threads.push_back(thread);
     threads.push_back(thread);
-    thread->start(TP_normal, true, true);
+    thread->start(TP_normal, true);
   }
   }
 
 
   // Now join all the threads.
   // Now join all the threads.
@@ -117,5 +117,6 @@ main(int argc, char *argv[]) {
     (*ti)->join();
     (*ti)->join();
   }
   }
 
 
-  exit(0);
+  Thread::prepare_for_exit();
+  return 0;
 }
 }

+ 19 - 3
panda/src/pipeline/test_delete.cxx

@@ -66,9 +66,22 @@ public:
   ALLOC_DELETED_CHAIN(Doober);
   ALLOC_DELETED_CHAIN(Doober);
 
 
   int _counter;
   int _counter;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "Doober");
+  }
+
+private:
+  static TypeHandle _type_handle;
 };
 };
 typedef pvector<Doober *> Doobers;
 typedef pvector<Doober *> Doobers;
 
 
+TypeHandle Doober::_type_handle;
+
 
 
 class MyThread : public Thread {
 class MyThread : public Thread {
 public:
 public:
@@ -116,6 +129,8 @@ public:
 
 
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
+  Doober::init_type();
+
   OUTPUT(nout << "Making " << number_of_threads << " threads.\n");
   OUTPUT(nout << "Making " << number_of_threads << " threads.\n");
 
 
   typedef pvector< PT(MyThread) > Threads;
   typedef pvector< PT(MyThread) > Threads;
@@ -123,13 +138,13 @@ main(int argc, char *argv[]) {
 
 
   PT(MyThread) thread = new MyThread("a");
   PT(MyThread) thread = new MyThread("a");
   threads.push_back(thread);
   threads.push_back(thread);
-  thread->start(TP_normal, true, true);
+  thread->start(TP_normal, true);
 
 
   for (int i = 1; i < number_of_threads; ++i) {
   for (int i = 1; i < number_of_threads; ++i) {
     char name = 'a' + i;
     char name = 'a' + i;
     PT(MyThread) thread = new MyThread(string(1, name));
     PT(MyThread) thread = new MyThread(string(1, name));
     threads.push_back(thread);
     threads.push_back(thread);
-    thread->start(TP_normal, true, true);
+    thread->start(TP_normal, true);
   }
   }
 
 
   // Now join all the threads.
   // Now join all the threads.
@@ -138,5 +153,6 @@ main(int argc, char *argv[]) {
     (*ti)->join();
     (*ti)->join();
   }
   }
 
 
-  exit(0);
+  Thread::prepare_for_exit();
+  return (0);
 }
 }

+ 7 - 4
panda/src/pipeline/test_diners.cxx

@@ -113,6 +113,7 @@ private:
     }
     }
     room_mutex.lock();
     room_mutex.lock();
     --room_occupancy;
     --room_occupancy;
+    cerr << "clearing philosopher " << _id << "\n";
     phils[_id] = (philosopher*)0L;
     phils[_id] = (philosopher*)0L;
     room_condition.signal();
     room_condition.signal();
     room_mutex.release();
     room_mutex.release();
@@ -150,7 +151,7 @@ main(int argc, char *argv[]) {
   }
   }
   for (i=0; i<N_DINERS; ++i) {
   for (i=0; i<N_DINERS; ++i) {
     phils[i] = new philosopher(i);
     phils[i] = new philosopher(i);
-    phils[i]->start(TP_normal, false, false);
+    phils[i]->start(TP_normal, false);
   }
   }
   room_occupancy = N_DINERS;
   room_occupancy = N_DINERS;
 
 
@@ -159,10 +160,12 @@ main(int argc, char *argv[]) {
   double end_time = start_time + run_time;
   double end_time = start_time + run_time;
 
 
   while (!has_run_time || clock->get_short_time() < end_time) {
   while (!has_run_time || clock->get_short_time() < end_time) {
-    while (room_occupancy == N_DINERS) {
+    if (room_occupancy == N_DINERS) {
       PRINTMSG(cerr << "main thread about to block " << room_occupancy
       PRINTMSG(cerr << "main thread about to block " << room_occupancy
                << "\n");
                << "\n");
-      room_condition.wait();
+      while (room_occupancy == N_DINERS) {
+        room_condition.wait();
+      }
     }
     }
     // hmm.. someone left the room.
     // hmm.. someone left the room.
     room_mutex.release();
     room_mutex.release();
@@ -176,7 +179,7 @@ main(int argc, char *argv[]) {
         break;
         break;
     assert(i != N_DINERS);
     assert(i != N_DINERS);
     phils[i] = new philosopher(i);
     phils[i] = new philosopher(i);
-    phils[i]->start(TP_normal, false, false);
+    phils[i]->start(TP_normal, false);
     ++room_occupancy;
     ++room_occupancy;
   }
   }
 
 

+ 2 - 2
panda/src/pipeline/test_mutex.cxx

@@ -60,8 +60,8 @@ main(int argc, char *argv[]) {
   MyThread *b = new MyThread("b", _m1, 0.9);
   MyThread *b = new MyThread("b", _m1, 0.9);
 
 
   cerr << "Starting threads.\n";
   cerr << "Starting threads.\n";
-  a->start(TP_normal, true, true);
-  b->start(TP_normal, true, true);
+  a->start(TP_normal, true);
+  b->start(TP_normal, true);
 
 
   a->join();
   a->join();
   b->join();
   b->join();

+ 44 - 0
panda/src/pipeline/test_setjmp.cxx

@@ -0,0 +1,44 @@
+// Filename: test_setjmp.cxx
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pandabase.h"
+
+#include <setjmp.h>
+
+int
+main(int argc, char *argv[]) {
+  jmp_buf buf1, buf2;
+  char * volatile scratch;
+
+  setjmp(buf1);
+  scratch = (char *)alloca(1024);
+  setjmp(buf2);
+
+  size_t word_size = sizeof(buf1[0]);
+  size_t num_words = sizeof(buf1) / word_size;
+  
+  cerr << num_words << " words of " << word_size << " bytes\n";
+  for (size_t i = 0; i < num_words; ++i) {
+    cerr << "  word " << i << ": " << (void *)buf1[i] << " vs. "
+         << (void *)buf2[i] << ", delta " << buf1[i] - buf2[i] << "\n";
+  }
+  cerr << "scratch = " << (void *)scratch << "\n";
+  cerr << "scratch end = " << (void *)(scratch + 1024) << "\n";
+    
+  return 0;
+}

+ 1 - 1
panda/src/pipeline/test_threaddata.cxx

@@ -67,7 +67,7 @@ main() {
   for (int i = 0; i < 10; i++) {
   for (int i = 0; i < 10; i++) {
     string name = string("thread_") + (char)(i + 'a');
     string name = string("thread_") + (char)(i + 'a');
     PT(Thread) thread = new ThreadWithData(name, i);
     PT(Thread) thread = new ThreadWithData(name, i);
-    if (!thread->start(TP_low, false, false)) {
+    if (!thread->start(TP_low, true)) {
       MutexHolder holder(cout_mutex);
       MutexHolder holder(cout_mutex);
       cout << "Unable to start " << name << ".\n";
       cout << "Unable to start " << name << ".\n";
     } else {
     } else {

+ 45 - 18
panda/src/pipeline/thread.I

@@ -252,9 +252,36 @@ sleep(double seconds) {
   ThreadImpl::sleep(seconds);
   ThreadImpl::sleep(seconds);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::field_yield
+//       Access: Published, Static
+//  Description: Suspends the current thread for the rest of the
+//               current epoch.
+////////////////////////////////////////////////////////////////////
+INLINE void Thread::
+force_yield() {
+  TAU_PROFILE("void Thread::yield()", " ", TAU_USER);
+  ThreadImpl::yield();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::consider_yield
+//       Access: Published, Static
+//  Description: Possibly suspends the current thread for the rest of
+//               the current epoch, if it has run for enough this
+//               epoch.  This is especially important for the simple
+//               thread implementation, which relies on cooperative
+//               yields like this.
+////////////////////////////////////////////////////////////////////
+INLINE void Thread::
+consider_yield() {
+  TAU_PROFILE("void Thread::consider_yield()", " ", TAU_USER);
+  ThreadImpl::consider_yield();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::is_started
 //     Function: Thread::is_started
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the thread has been started, false if
 //  Description: Returns true if the thread has been started, false if
 //               it has not, or if join() has already been called.
 //               it has not, or if join() has already been called.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -263,24 +290,9 @@ is_started() const {
   return _started;
   return _started;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: Thread::interrupt
-//       Access: Public
-//  Description: Sends an interrupt message to the thread.  This will
-//               interrupt any blocking-type system calls the thread
-//               may be waiting on, such as I/O, so that the thread
-//               may continue some other processing.  The specific
-//               behavior is implementation dependent.
-////////////////////////////////////////////////////////////////////
-INLINE void Thread::
-interrupt() {
-  nassertv(_started);
-  _impl.interrupt();
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::join
 //     Function: Thread::join
-//       Access: Public
+//       Access: Published
 //  Description: Blocks the calling process until the thread
 //  Description: Blocks the calling process until the thread
 //               terminates.  If the thread has already terminated,
 //               terminates.  If the thread has already terminated,
 //               this returns immediately.
 //               this returns immediately.
@@ -294,9 +306,24 @@ join() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::preempt
+//       Access: Published
+//  Description: Indicates that this thread should run as soon as
+//               possible, preemptying any other threads that may be
+//               scheduled to run.  This may not be implemented on
+//               every platform.
+////////////////////////////////////////////////////////////////////
+INLINE void Thread::
+preempt() {
+  if (_started) {
+    _impl.preempt();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::prepare_for_exit
 //     Function: Thread::prepare_for_exit
-//       Access: Public
+//       Access: Published
 //  Description: Should be called by the main thread just before
 //  Description: Should be called by the main thread just before
 //               exiting the program, this blocks until any remaining
 //               exiting the program, this blocks until any remaining
 //               thread cleanup has finished.
 //               thread cleanup has finished.

+ 5 - 1
panda/src/pipeline/thread.cxx

@@ -166,7 +166,11 @@ start(ThreadPriority priority, bool joinable) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Thread::
 void Thread::
 init_main_thread() {
 init_main_thread() {
-  if (_main_thread == (Thread *)NULL) {
+  // There is a chance of mutual recursion at startup.  The count
+  // variable here attempts to protect against that.
+  static int count = 0;
+  ++count;
+  if (count == 1 && _main_thread == (Thread *)NULL) {
     _main_thread = new MainThread;
     _main_thread = new MainThread;
     _main_thread->ref();
     _main_thread->ref();
   }
   }

+ 6 - 2
panda/src/pipeline/thread.h

@@ -77,15 +77,17 @@ PUBLISHED:
   INLINE static bool is_threading_supported();
   INLINE static bool is_threading_supported();
   BLOCKING INLINE static void sleep(double seconds);
   BLOCKING INLINE static void sleep(double seconds);
 
 
+  BLOCKING INLINE static void force_yield();
+  BLOCKING INLINE static void consider_yield();
+
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 
   INLINE bool is_started() const;
   INLINE bool is_started() const;
 
 
   bool start(ThreadPriority priority, bool joinable);
   bool start(ThreadPriority priority, bool joinable);
-  INLINE void interrupt();
   BLOCKING INLINE void join();
   BLOCKING INLINE void join();
+  INLINE void preempt();
 
 
-public:
   INLINE static void prepare_for_exit();
   INLINE static void prepare_for_exit();
 
 
 private:
 private:
@@ -132,6 +134,8 @@ private:
   friend class ThreadDummyImpl;
   friend class ThreadDummyImpl;
   friend class ThreadWin32Impl;
   friend class ThreadWin32Impl;
   friend class ThreadPosixImpl;
   friend class ThreadPosixImpl;
+  friend class ThreadSimpleImpl;
+  friend class MainThread;
 };
 };
 
 
 INLINE ostream &operator << (ostream &out, const Thread &thread);
 INLINE ostream &operator << (ostream &out, const Thread &thread);

+ 42 - 5
panda/src/pipeline/threadDummyImpl.I

@@ -35,6 +35,17 @@ INLINE ThreadDummyImpl::
 ~ThreadDummyImpl() {
 ~ThreadDummyImpl() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadDummyImpl::setup_main_thread
+//       Access: Public
+//  Description: Called for the main thread only, which has been
+//               already started, to fill in the values appropriate to
+//               that thread.
+////////////////////////////////////////////////////////////////////
+void ThreadDummyImpl::
+setup_main_thread() {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadDummyImpl::start
 //     Function: ThreadDummyImpl::start
 //       Access: Public
 //       Access: Public
@@ -46,21 +57,21 @@ start(ThreadPriority, bool) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ThreadDummyImpl::interrupt
+//     Function: ThreadDummyImpl::join
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void ThreadDummyImpl::
 INLINE void ThreadDummyImpl::
-interrupt() {
+join() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ThreadDummyImpl::join
+//     Function: ThreadDummyImpl::preempt
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void ThreadDummyImpl::
 INLINE void ThreadDummyImpl::
-join() {
+preempt() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -101,5 +112,31 @@ is_threading_supported() {
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void ThreadDummyImpl::
 INLINE void ThreadDummyImpl::
-sleep(double) {
+sleep(double seconds) {
+#ifdef WIN32
+  Sleep((int)(seconds * 1000));
+#else
+  struct timespec rqtp;
+  rqtp.tv_sec = time_t(seconds);
+  rqtp.tv_nsec = long((seconds - (double)rqtp.tv_sec) * 1000000000.0);
+  nanosleep(&rqtp, NULL);
+#endif  // WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadDummyImpl::yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadDummyImpl::
+yield() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadDummyImpl::consider_yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadDummyImpl::
+consider_yield() {
 }
 }

+ 4 - 1
panda/src/pipeline/threadDummyImpl.h

@@ -40,9 +40,10 @@ public:
   INLINE ThreadDummyImpl(Thread *parent_obj);
   INLINE ThreadDummyImpl(Thread *parent_obj);
   INLINE ~ThreadDummyImpl();
   INLINE ~ThreadDummyImpl();
 
 
+  INLINE void setup_main_thread();
   INLINE bool start(ThreadPriority priority, bool joinable);
   INLINE bool start(ThreadPriority priority, bool joinable);
-  INLINE void interrupt();
   INLINE void join();
   INLINE void join();
+  INLINE void preempt();
 
 
   INLINE static void prepare_for_exit();
   INLINE static void prepare_for_exit();
 
 
@@ -50,6 +51,8 @@ public:
   INLINE static void bind_thread(Thread *thread);
   INLINE static void bind_thread(Thread *thread);
   INLINE static bool is_threading_supported();
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
   INLINE static void sleep(double seconds);
+  INLINE static void yield();
+  INLINE static void consider_yield();
 };
 };
 
 
 #include "threadDummyImpl.I"
 #include "threadDummyImpl.I"

+ 5 - 0
panda/src/pipeline/threadImpl.h

@@ -27,6 +27,11 @@
 #include "threadDummyImpl.h"
 #include "threadDummyImpl.h"
 typedef ThreadDummyImpl ThreadImpl;
 typedef ThreadDummyImpl ThreadImpl;
 
 
+#elif defined(THREAD_SIMPLE_IMPL)
+
+#include "threadSimpleImpl.h"
+typedef ThreadSimpleImpl ThreadImpl;
+
 #elif defined(THREAD_WIN32_IMPL)
 #elif defined(THREAD_WIN32_IMPL)
 
 
 #include "threadWin32Impl.h"
 #include "threadWin32Impl.h"

+ 28 - 0
panda/src/pipeline/threadPosixImpl.I

@@ -31,6 +31,15 @@ ThreadPosixImpl(Thread *parent_obj) :
   _status = S_new;
   _status = S_new;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadPosixImpl::preempt
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadPosixImpl::
+preempt() {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadPosixImpl::prepare_for_exit
 //     Function: ThreadPosixImpl::prepare_for_exit
 //       Access: Public
 //       Access: Public
@@ -93,3 +102,22 @@ sleep(double seconds) {
   rqtp.tv_nsec = long((seconds - (double)rqtp.tv_sec) * 1000000000.0);
   rqtp.tv_nsec = long((seconds - (double)rqtp.tv_sec) * 1000000000.0);
   nanosleep(&rqtp, NULL);
   nanosleep(&rqtp, NULL);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadPosixImpl::yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadPosixImpl::
+yield() {
+  sleep(0.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadPosixImpl::consider_yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadPosixImpl::
+consider_yield() {
+}

+ 12 - 13
panda/src/pipeline/threadPosixImpl.cxx

@@ -51,6 +51,18 @@ ThreadPosixImpl::
   _mutex.release();
   _mutex.release();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadPosixImpl::setup_main_thread
+//       Access: Public
+//  Description: Called for the main thread only, which has been
+//               already started, to fill in the values appropriate to
+//               that thread.
+////////////////////////////////////////////////////////////////////
+void ThreadPosixImpl::
+setup_main_thread() {
+  _status = S_running;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadPosixImpl::start
 //     Function: ThreadPosixImpl::start
 //       Access: Public
 //       Access: Public
@@ -151,19 +163,6 @@ start(ThreadPriority priority, bool joinable) {
   return true;
   return true;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: ThreadPosixImpl::interrupt
-//       Access: Public
-//  Description: Sends an interrupt message to the thread.  This will
-//               interrupt any blocking-type system calls the thread
-//               may be waiting on, such as I/O, so that the thread
-//               may continue some other processing.  The specific
-//               behavior is implementation dependent.
-////////////////////////////////////////////////////////////////////
-void ThreadPosixImpl::
-interrupt() {
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadPosixImpl::join
 //     Function: ThreadPosixImpl::join
 //       Access: Public
 //       Access: Public

+ 4 - 1
panda/src/pipeline/threadPosixImpl.h

@@ -41,9 +41,10 @@ public:
   INLINE ThreadPosixImpl(Thread *parent_obj);
   INLINE ThreadPosixImpl(Thread *parent_obj);
   ~ThreadPosixImpl();
   ~ThreadPosixImpl();
 
 
+  void setup_main_thread();
   bool start(ThreadPriority priority, bool joinable);
   bool start(ThreadPriority priority, bool joinable);
-  void interrupt();
   void join();
   void join();
+  INLINE void preempt();
 
 
   INLINE static void prepare_for_exit();
   INLINE static void prepare_for_exit();
 
 
@@ -51,6 +52,8 @@ public:
   INLINE static void bind_thread(Thread *thread);
   INLINE static void bind_thread(Thread *thread);
   INLINE static bool is_threading_supported();
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
   INLINE static void sleep(double seconds);
+  INLINE static void yield();
+  INLINE static void consider_yield();
 
 
 private:
 private:
   static void *root_func(void *data);
   static void *root_func(void *data);

+ 108 - 0
panda/src/pipeline/threadSimpleImpl.I

@@ -0,0 +1,108 @@
+// Filename: threadSimpleImpl.I
+// Created by:  drose (18Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: ThreadSimpleImpl::get_current_thread
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE Thread *ThreadSimpleImpl::
+get_current_thread() {
+  return ThreadSimpleManager::get_global_ptr()->get_current_thread()->_parent_obj;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::bind_thread
+//       Access: Public, Static
+//  Description: Associates the indicated Thread object with the
+//               currently-executing thread.  You should not call this
+//               directly; use Thread::bind_thread() instead.
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadSimpleImpl::
+bind_thread(Thread *) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::is_threading_supported
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool ThreadSimpleImpl::
+is_threading_supported() {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::sleep
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadSimpleImpl::
+sleep(double seconds) {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  ThreadSimpleImpl *thread = manager->get_current_thread();
+  thread->sleep_this(seconds);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadSimpleImpl::
+yield() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  ThreadSimpleImpl *thread = manager->get_current_thread();
+  thread->yield_this();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::consider_yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadSimpleImpl::
+consider_yield() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  ThreadSimpleImpl *thread = manager->get_current_thread();
+  thread->consider_yield_this();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::consider_yield_this
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadSimpleImpl::
+consider_yield_this() {
+  double now = _manager->get_current_time();
+  if (now - _start_time > _time_per_epoch) {
+    yield_this();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::get_start_time
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE double ThreadSimpleImpl::
+get_start_time() const {
+  return _start_time;
+}

+ 273 - 0
panda/src/pipeline/threadSimpleImpl.cxx

@@ -0,0 +1,273 @@
+// Filename: threadSimpleImpl.cxx
+// Created by:  drose (18Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "threadSimpleImpl.h"
+#include "threadSimpleManager.h"
+#include "thread.h"
+
+ThreadSimpleImpl *volatile ThreadSimpleImpl::_st_this;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ThreadSimpleImpl::
+ThreadSimpleImpl(Thread *parent_obj) :
+  _parent_obj(parent_obj)
+{
+  _status = S_new;
+  _joinable = false;
+  _time_per_epoch = 0.0;
+  _start_time = 0.0;
+
+  _stack_size = (size_t)thread_stack_size;
+
+  // We allocate the requested stack size, plus an additional tiny
+  // buffer to allow room for the code to access values on the
+  // currently executing stack frame at the time we switch the stack.
+  _stack = (unsigned char *)malloc(_stack_size + 0x100);
+
+  // Save this pointer for convenience.
+  _manager = ThreadSimpleManager::get_global_ptr();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ThreadSimpleImpl::
+~ThreadSimpleImpl() {
+  if (thread_cat.is_debug()) {
+    thread_cat.debug() 
+      << "Deleting thread " << _parent_obj->get_name() << "\n";
+  }
+  nassertv(_status != S_running);
+
+  free(_stack);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::setup_main_thread
+//       Access: Public
+//  Description: Called for the main thread only, which has been
+//               already started, to fill in the values appropriate to
+//               that thread.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+setup_main_thread() {
+  _status = S_running;
+  _time_per_epoch = 0.05;
+
+  _manager->set_current_thread(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::start
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool ThreadSimpleImpl::
+start(ThreadPriority priority, bool joinable) {
+  if (thread_cat.is_debug()) {
+    thread_cat.debug() << "Starting " << *_parent_obj << "\n";
+  }
+
+  nassertr(_status == S_new, false);
+
+  _joinable = joinable;
+  _status = S_running;
+
+  switch (priority) {
+  case TP_low:
+    _time_per_epoch = 0.02;
+    break;
+    
+  case TP_normal:
+    _time_per_epoch = 0.05;
+    break;
+    
+  case TP_high:
+    _time_per_epoch = 0.20;
+    break;
+
+  case TP_urgent:
+    _time_per_epoch = 0.50;
+    break;
+  }
+
+  // We'll keep the reference count upped while the thread is running.
+  // When the thread finishes, we'll drop the reference count.
+  _parent_obj->ref();
+
+#ifdef HAVE_PYTHON
+  // Query the current Python thread state.
+  _python_state = PyThreadState_Swap(NULL);
+  PyThreadState_Swap(_python_state);
+#endif  // HAVE_PYTHON
+
+  setup_context();
+
+  _manager->enqueue_ready(this);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::join
+//       Access: Public
+//  Description: Blocks the calling process until the thread
+//               terminates.  If the thread has already terminated,
+//               this returns immediately.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+join() {
+  nassertv(_joinable);
+  if (_status == S_running) {
+    ThreadSimpleImpl *thread = _manager->get_current_thread();
+    _joining_threads.push_back(thread);
+    _manager->next_context();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::preempt
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+preempt() {
+  _manager->preempt(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::prepare_for_exit
+//       Access: Public, Static
+//  Description: Waits for all threads to terminate.  Normally this is
+//               called only from the main thread.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+prepare_for_exit() {
+  ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+  manager->prepare_for_exit();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::sleep_this
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+sleep_this(double seconds) {
+  _manager->enqueue_sleep(this, seconds);
+  _manager->next_context();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::yield_this
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+yield_this() {
+  _manager->enqueue_ready(this);
+  _manager->next_context();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::setup_context
+//       Access: Private
+//  Description: Fills the _context with an appropriate context buffer
+//               and an appropriate stack reserved for the thread.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+setup_context() {
+  jmp_buf orig_stack;
+  if (setjmp(orig_stack) == 0) {
+    // First, switch to the new stack.  This requires temporarily saving
+    // the this pointer as a static.
+    _st_this = this;
+
+    // Save the current context using setjmp().  This saves out all of
+    // the processor register values, though it doesn't muck with the
+    // stack.
+    jmp_buf temp;
+    if (setjmp(temp) == 0) {
+      // This is the initial return from setjmp.  Still the original
+      // stack.
+
+      // Now we overwrite the stack pointer value in the saved
+      // register context.  This doesn't work with all implementations
+      // of setjmp/longjmp.
+      ((void *&)temp[JB_SP]) = (_st_this->_stack + _st_this->_stack_size);
+
+      // And finally, we place ourselves on the new stack by using
+      // longjmp() to reload the saved (and modified) context.
+      longjmp(temp, 1);
+    }
+
+    // This is the second return from setjmp.  Now we're on the new
+    // stack.
+    setup_context_2(_st_this);
+    
+    // Now restore the original stack and return.
+    longjmp(orig_stack, 1);
+  }
+
+  // By now we are back to the original stack.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::setup_context_3
+//       Access: Private
+//  Description: More continuation of setup_context().  Again, making
+//               this a separate function helps defeat the compiler
+//               optimizer.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+setup_context_3() {
+#ifdef HAVE_PYTHON
+  PyThreadState_Swap(_python_state);
+#endif  // HAVE_PYTHON
+
+  // Here we are executing within the thread.  Run the thread_main
+  // function defined for this thread.
+  _parent_obj->thread_main();
+  
+  // Now we have completed the thread.
+  _status = S_finished;
+
+  // Any threads that were waiting to join with this thread now become ready.
+  JoiningThreads::iterator jti;
+  for (jti = _joining_threads.begin(); jti != _joining_threads.end(); ++jti) {
+    _manager->enqueue_ready(*jti);
+  }
+  _joining_threads.clear();
+
+  _manager->enqueue_finished(this);
+  _manager->next_context();
+  
+  // Shouldn't get here.
+  abort();
+}
+
+#endif  // THREAD_SIMPLE_IMPL

+ 151 - 0
panda/src/pipeline/threadSimpleImpl.h

@@ -0,0 +1,151 @@
+// Filename: threadSimpleImpl.h
+// Created by:  drose (18Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 THREADSIMPLEIMPL_H
+#define THREADSIMPLEIMPL_H
+
+#include "pandabase.h"
+#include "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "pnotify.h"
+#include "threadPriority.h"
+#include "pvector.h"
+
+#ifdef HAVE_PYTHON
+
+#undef _POSIX_C_SOURCE
+#include <Python.h>
+
+#endif  // HAVE_PYTHON
+
+#include <setjmp.h>
+
+#if !defined(JB_SP) && defined(IS_OSX) && defined(__i386__)
+// We have determined this value empirically, via test_setjmp.cxx in
+// this directory.
+#define JB_SP 9
+#endif
+
+class Thread;
+class ThreadSimpleManager;
+class MutexSimpleImpl;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ThreadSimpleImpl
+// Description : This is a trivial threading implementation for
+//               applications that don't desire full OS-managed
+//               threading.  It is a user-space implementation of
+//               threads implemented via setjmp/longjmp, and therefore
+//               it cannot take advantage of multiple CPU's (the
+//               application will always run on a single CPU,
+//               regardless of the number of threads you spawn).
+//
+//               However, since context switching is entirely
+//               cooperative, synchronization primitives like mutexes
+//               and condition variables aren't necessary, and the
+//               Mutex and ConditionVar classes are compiled into
+//               trivial no-op classes, which can reduce overhead
+//               substantially compared to a truly threaded
+//               application.
+//
+//               Be sure that every thread calls
+//               Thread::consider_yield() occasionally, or it will
+//               starve the rest of the running threads.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ThreadSimpleImpl {
+public:
+  ThreadSimpleImpl(Thread *parent_obj);
+  ~ThreadSimpleImpl();
+
+  void setup_main_thread();
+  bool start(ThreadPriority priority, bool joinable);
+  void join();
+  void preempt();
+
+  static void prepare_for_exit();
+
+  INLINE static Thread *get_current_thread();
+  INLINE static void bind_thread(Thread *thread);
+  INLINE static bool is_threading_supported();
+  INLINE static void sleep(double seconds);
+  INLINE static void yield();
+  INLINE static void consider_yield();
+
+  void sleep_this(double seconds);
+  void yield_this();
+  INLINE void consider_yield_this();
+
+  INLINE double get_start_time() const;
+
+private:
+  void setup_context();
+  static void setup_context_2(ThreadSimpleImpl *self);
+  void setup_context_3();
+
+private:
+  enum Status {
+    S_new,
+    S_running,
+    S_finished,
+    S_killed,
+  };
+
+  Thread *_parent_obj;
+  bool _joinable;
+  Status _status;
+
+  // The (approx) amount of time this thread is allowed to run each
+  // epoch.
+  double _time_per_epoch;
+
+  // This serves both as the time at which the current thread started
+  // to run, and also records the time at which a sleeping thread
+  // should wake up.
+  double _start_time;
+
+  jmp_buf _context;
+  unsigned char *_stack;
+  size_t _stack_size;
+
+#ifdef HAVE_PYTHON
+  // If we might be working with Python, we have to manage the Python
+  // thread state as we switch contexts.
+  PyThreadState *_python_state;
+#endif  // HAVE_PYTHON
+
+  // Threads that are waiting for this thread to finish.
+  typedef pvector<ThreadSimpleImpl *> JoiningThreads;
+  JoiningThreads _joining_threads;
+
+  ThreadSimpleManager *_manager;
+  static ThreadSimpleImpl *volatile _st_this;
+
+  friend class ThreadSimpleManager;
+};
+
+// We include this down here to avoid the circularity problem.
+/* okcircular */
+#include "threadSimpleManager.h"
+
+#include "threadSimpleImpl.I"
+
+#endif // THREAD_SIMPLE_IMPL
+
+#endif

+ 54 - 0
panda/src/pipeline/threadSimpleImpl_no_opt_.cxx

@@ -0,0 +1,54 @@
+// Filename: threadSimpleImpl_setup_context.cxx
+// Created by:  drose (20Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "threadSimpleImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleImpl::setup_context_2
+//       Access: Private, Static
+//  Description: Continuation of setup_context().  We have this as a
+//               separate method so we can ensure that the stack gets
+//               set up with our this pointer properly.  It is defined
+//               in a separate file so we can disable compiler
+//               optimizations on this one method (gcc prefers to
+//               disable optimizations globally on the command line,
+//               not via pragmas).
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleImpl::
+setup_context_2(ThreadSimpleImpl *self) {
+  ThreadSimpleImpl *volatile v_self = self;
+  if (setjmp(self->_context) == 0) {
+    // The _context is now set up and ready to run.  Now we can
+    // return.
+    return;
+  }
+   
+  // Here we are executing within the thread.
+  v_self->setup_context_3();
+
+  // This code will never run, but we need to have some real code
+  // here, to force the compiler to build an appropriate stack frame
+  // for the above call (otherwise it might optimize the stack frame
+  // away).
+  *v_self = 0;
+  abort();
+}
+
+#endif  // THREAD_SIMPLE_IMPL

+ 64 - 0
panda/src/pipeline/threadSimpleManager.I

@@ -0,0 +1,64 @@
+// Filename: threadSimpleManager.I
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: ThreadSimpleManager::get_current_thread
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ThreadSimpleImpl *ThreadSimpleManager::
+get_current_thread() {
+  return _current_thread;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::get_current_time
+//       Access: Public
+//  Description: Returns elapsed time in seconds from some undefined
+//               epoch, via whatever clock the manager is using for
+//               all thread timing.
+////////////////////////////////////////////////////////////////////
+INLINE double ThreadSimpleManager::
+get_current_time() const {
+  return _clock->get_short_raw_time();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::get_global_ptr
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ThreadSimpleManager *ThreadSimpleManager::
+get_global_ptr() {
+  if (!_pointers_initialized) {
+    init_pointers();
+  }
+  return _global_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::CompareStartTime::operator()
+//       Access: Public
+//  Description: STL function object to sort the priority queue of
+//               sleeping threads.
+////////////////////////////////////////////////////////////////////
+INLINE bool ThreadSimpleManager::CompareStartTime::
+operator ()(ThreadSimpleImpl *a, ThreadSimpleImpl *b) const {
+  return a->get_start_time() < b->get_start_time();
+}

+ 447 - 0
panda/src/pipeline/threadSimpleManager.cxx

@@ -0,0 +1,447 @@
+// Filename: threadSimpleManager.cxx
+// Created by:  drose (19Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "threadSimpleManager.h"
+#include "threadSimpleImpl.h"
+#include "blockerSimple.h"
+
+bool ThreadSimpleManager::_pointers_initialized;
+ThreadSimpleManager *ThreadSimpleManager::_global_ptr;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::Constructor
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ThreadSimpleManager::
+ThreadSimpleManager() {
+  _clock = TrueClock::get_global_ptr();
+  _waiting_for_exit = NULL;
+  nassertv(_current_thread == NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::enqueue_ready
+//       Access: Public
+//  Description: Adds the indicated thread to the ready queue.  The
+//               thread will be executed when its turn comes.  If the
+//               thread is not the currently executing thread, its
+//               _context should be filled appropriately.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+enqueue_ready(ThreadSimpleImpl *thread) {
+  _ready.push_back(thread);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::enqueue_sleep
+//       Access: Public
+//  Description: Adds the indicated thread to the sleep queue, until
+//               the indicated number of seconds have elapsed.  Then
+//               the thread will be automatically moved to the ready
+//               queue.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+enqueue_sleep(ThreadSimpleImpl *thread, double seconds) {
+  double now = get_current_time();
+  thread->_start_time = now + seconds;
+  _sleeping.push_back(thread);
+  push_heap(_sleeping.begin(), _sleeping.end(), CompareStartTime());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::enqueue_block
+//       Access: Public
+//  Description: Adds the indicated thread to the blocked queue for
+//               the indicated blocker.  The thread will be awoken by
+//               a later call to unblock_one() or unblock_all().
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+enqueue_block(ThreadSimpleImpl *thread, BlockerSimple *blocker) {
+  _blocked[blocker].push_back(thread);
+  blocker->_flags |= BlockerSimple::F_has_waiters;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::unblock_one
+//       Access: Public
+//  Description: Unblocks one thread waiting on the indicated blocker,
+//               if any.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+unblock_one(BlockerSimple *blocker) {
+  Blocked::iterator bi = _blocked.find(blocker);
+  if (bi != _blocked.end()) {
+    nassertv(blocker->_flags & BlockerSimple::F_has_waiters);
+
+    FifoThreads &threads = (*bi).second;
+    nassertv(!threads.empty());
+    ThreadSimpleImpl *thread = threads.front();
+    threads.pop_front();
+    _ready.push_back(thread);
+    if (threads.empty()) {
+      blocker->_flags &= ~BlockerSimple::F_has_waiters;
+      _blocked.erase(bi);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::unblock_all
+//       Access: Public
+//  Description: Unblocks all threads waiting on the indicated
+//               blocker.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+unblock_all(BlockerSimple *blocker) {
+  Blocked::iterator bi = _blocked.find(blocker);
+  if (bi != _blocked.end()) {
+    nassertv(blocker->_flags & BlockerSimple::F_has_waiters);
+
+    FifoThreads &threads = (*bi).second;
+    nassertv(!threads.empty());
+    while (!threads.empty()) {
+      ThreadSimpleImpl *thread = threads.front();
+      threads.pop_front();
+      _ready.push_back(thread);
+    }
+    blocker->_flags &= ~BlockerSimple::F_has_waiters;
+    _blocked.erase(bi);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::enqueue_finished
+//       Access: Public
+//  Description: Adds the indicated thread to the finished queue.  
+//               The manager will drop the reference count on the
+//               indicated thread at the next epoch.  (A thread can't
+//               drop its own reference count while it is running,
+//               since that might deallocate its own stack.)
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+enqueue_finished(ThreadSimpleImpl *thread) {
+  _finished.push_back(thread);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::preempt
+//       Access: Public
+//  Description: Moves the indicated thread to the head of the ready
+//               queue.  If it is not already on the ready queue, does
+//               nothing.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+preempt(ThreadSimpleImpl *thread) {
+  FifoThreads::iterator ti;
+  ti = find(_ready.begin(), _ready.end(), thread);
+  if (ti != _ready.end()) {
+    _ready.erase(ti);
+    _ready.push_front(thread);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::next_context
+//       Access: Public
+//  Description: Switches out the currently executing thread and
+//               chooses a new thread for execution.  Before calling
+//               this, the current thread should have already
+//               re-enqueued itself with a call to enqueue(), if it
+//               intends to run again.
+//
+//               This will fill in the current thread's _context
+//               member appropriately, and then change the global
+//               current_thread pointer.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+next_context() {
+  // Mark the current thread's resume point.
+
+#ifdef HAVE_PYTHON
+  // Query the current Python thread state.
+  _current_thread->_python_state = PyThreadState_Swap(NULL);
+  PyThreadState_Swap(_current_thread->_python_state);
+#endif  // HAVE_PYTHON
+
+  if (setjmp(_current_thread->_context) != 0) {
+    // We have returned from a longjmp, and are now resuming the
+    // current thread.
+#ifdef HAVE_PYTHON
+    PyThreadState_Swap(_current_thread->_python_state);
+#endif  // HAVE_PYTHON
+
+    return;
+  }
+
+  while (!_finished.empty() && _finished.front() != _current_thread) {
+    ThreadSimpleImpl *finished_thread = _finished.front();
+    _finished.pop_front();
+    unref_delete(finished_thread->_parent_obj);
+  }
+
+  _current_thread = NULL;
+
+  double now = get_current_time();
+
+  if (!_sleeping.empty()) {
+    wake_sleepers(now);
+  }
+
+  // Choose a new thread to execute.
+  while (_ready.empty()) {
+    // No threads are ready.  They must all be sleeping.
+    if (_sleeping.empty()) {
+      // No threads at all!
+      if (!_blocked.empty()) {
+        thread_cat.error()
+          << "Deadlock!  All threads blocked.\n";
+        report_deadlock();
+        abort();
+      }
+
+      // All threads have finished execution.
+      if (_waiting_for_exit != NULL) {
+        // And one thread--presumably the main thread--was waiting for
+        // that.
+        _ready.push_back(_waiting_for_exit);
+        _waiting_for_exit = NULL;
+        break;
+      }
+
+      // No threads are queued anywhere.  This is kind of an error,
+      // since normally the main thread, at least, should be queued
+      // somewhere.
+      thread_cat.error()
+        << "All threads disappeared!\n";
+      exit(0);
+    }
+
+    double wait = _sleeping.front()->_start_time - now;
+    if (wait > 0.0) {
+      if (thread_cat.is_spam()) {
+        thread_cat.spam()
+          << "Sleeping " << wait << " seconds\n";
+      }
+      system_sleep(wait);
+    }
+    now = get_current_time();
+    wake_sleepers(now);
+  }
+
+  _current_thread = _ready.front();
+  _ready.pop_front();
+  _current_thread->_start_time = now;
+
+  // All right, the thread is ready to roll.  Begin.
+  if (thread_cat.is_spam()) {
+    thread_cat.spam()
+      << "Switching to " << *_current_thread->_parent_obj << "\n";
+  }
+  longjmp(_current_thread->_context, 1);
+
+  // Shouldn't get here.
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::prepare_for_exit
+//       Access: Public
+//  Description: Blocks until all running threads (other than the
+//               current thread) have finished.  You should probably
+//               only call this from the main thread.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+prepare_for_exit() {
+  nassertv(_waiting_for_exit == NULL);
+  _waiting_for_exit = _current_thread;
+
+  // At this point, any non-joinable threads on any of the queues are
+  // automatically killed.
+  kill_non_joinable(_ready);
+
+  Blocked::iterator bi = _blocked.begin();
+  while (bi != _blocked.end()) {
+    Blocked::iterator bnext = bi;
+    ++bnext;
+    BlockerSimple *blocker = (*bi).first;
+    FifoThreads &threads = (*bi).second;
+    kill_non_joinable(threads);
+    if (threads.empty()) {
+      blocker->_flags &= ~BlockerSimple::F_has_waiters;
+      _blocked.erase(bi);
+    }
+    bi = bnext;
+  }
+
+  kill_non_joinable(_sleeping);
+
+  next_context();
+
+  while (!_finished.empty() && _finished.front() != _current_thread) {
+    ThreadSimpleImpl *finished_thread = _finished.front();
+    _finished.pop_front();
+    unref_delete(finished_thread->_parent_obj);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::set_current_thread
+//       Access: Public
+//  Description: Sets the initial value of the current_thread pointer,
+//               i.e. the main thread.  It is valid to call this
+//               method only exactly once.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+set_current_thread(ThreadSimpleImpl *current_thread) {
+  nassertv(_current_thread == (ThreadSimpleImpl *)NULL);
+  _current_thread = current_thread;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::init_pointers
+//       Access: Private, Static
+//  Description: Should be called at startup to initialize the
+//               simple threading system.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+init_pointers() {
+  if (!_pointers_initialized) {
+    _pointers_initialized = true;
+    _global_ptr = new ThreadSimpleManager;
+    Thread::get_main_thread();
+
+#ifdef HAVE_PYTHON
+    // Ensure that the Python threading system is initialized and ready
+    // to go.
+    PyEval_InitThreads();
+#endif
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::wake_sleepers
+//       Access: Private
+//  Description: Moves any threads due to wake up from the sleeping
+//               queue to the ready queue.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+wake_sleepers(double now) {
+  while (!_sleeping.empty() && _sleeping.front()->_start_time <= now) {
+    ThreadSimpleImpl *thread = _sleeping.front();
+    pop_heap(_sleeping.begin(), _sleeping.end(), CompareStartTime());
+    _sleeping.pop_back();
+    _ready.push_back(thread);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::system_sleep
+//       Access: Private, Static
+//  Description: Calls the appropriate system sleep function to sleep
+//               the whole process for the indicated number of
+//               seconds.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+system_sleep(double seconds) {
+#ifdef WIN32
+  Sleep((int)(seconds * 1000));
+
+#else
+  struct timespec rqtp;
+  rqtp.tv_sec = time_t(seconds);
+  rqtp.tv_nsec = long((seconds - (double)rqtp.tv_sec) * 1000000000.0);
+  nanosleep(&rqtp, NULL);
+#endif  // WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::report_deadlock
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+report_deadlock() {
+  Blocked::iterator bi;
+  for (bi = _blocked.begin(); bi != _blocked.end(); ++bi) {
+    BlockerSimple *blocker = (*bi).first;
+    FifoThreads &threads = (*bi).second;
+    thread_cat.info()
+      << "On blocker " << blocker << ":\n";
+    FifoThreads::iterator ti;
+    for (ti = threads.begin(); ti != threads.end(); ++ti) {
+      ThreadSimpleImpl *thread = (*ti);
+      thread_cat.info()
+        << "  " << *thread->_parent_obj << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::kill_non_joinable
+//       Access: Private
+//  Description: Removes any non-joinable threads from the indicated
+//               queue and marks them killed.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+kill_non_joinable(ThreadSimpleManager::FifoThreads &threads) {
+  FifoThreads new_threads;
+  FifoThreads::iterator ti;
+  for (ti = threads.begin(); ti != threads.end(); ++ti) {
+    ThreadSimpleImpl *thread = (*ti);
+    if (thread->_joinable) {
+      new_threads.push_back(thread);
+    } else {
+      if (thread_cat.is_debug()) {
+        thread_cat.debug()
+          << "Killing " << *thread->_parent_obj << "\n";
+      }
+      thread->_status = ThreadSimpleImpl::S_killed;
+      enqueue_finished(thread);
+    }
+  }
+
+  threads.swap(new_threads);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSimpleManager::kill_non_joinable
+//       Access: Private
+//  Description: Removes any non-joinable threads from the indicated
+//               queue and marks them killed.
+////////////////////////////////////////////////////////////////////
+void ThreadSimpleManager::
+kill_non_joinable(ThreadSimpleManager::Sleeping &threads) {
+  Sleeping new_threads;
+  Sleeping::iterator ti;
+  for (ti = threads.begin(); ti != threads.end(); ++ti) {
+    ThreadSimpleImpl *thread = (*ti);
+    if (thread->_joinable) {
+      new_threads.push_back(thread);
+    } else {
+      if (thread_cat.is_debug()) {
+        thread_cat.debug()
+          << "Killing " << *thread->_parent_obj << "\n";
+      }
+      thread->_status = ThreadSimpleImpl::S_killed;
+      enqueue_finished(thread);
+    }
+  }
+  make_heap(new_threads.begin(), new_threads.end(), CompareStartTime());
+  threads.swap(new_threads);
+}

+ 121 - 0
panda/src/pipeline/threadSimpleManager.h

@@ -0,0 +1,121 @@
+// Filename: threadSimpleManager.h
+// Created by:  drose (18Jun07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 THREADSIMPLEMANAGER_H
+#define THREADSIMPLEMANAGER_H
+
+#include "pandabase.h"
+#include "selectThreadImpl.h"
+
+#ifdef THREAD_SIMPLE_IMPL
+
+#include "pdeque.h"
+#include "pmap.h"
+#include "pvector.h"
+#include "trueClock.h"
+#include <algorithm>
+
+class Thread;
+class ThreadSimpleImpl;
+class BlockerSimple;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ThreadSimpleManager
+// Description : This is the global object that selects the
+//               currently-active thread of the various
+//               ThreadSimpleImpl objects running, when the
+//               currently-active thread yields.
+//
+//               This class only exists when we are using the
+//               ThreadSimple implementation, which is to say, we are
+//               not using "real" threads.
+//
+//               Generally, you shouldn't be calling these methods
+//               directly.  Call the interfaces on Thread instead.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ThreadSimpleManager {
+private:
+  ThreadSimpleManager();
+
+public:
+  void enqueue_ready(ThreadSimpleImpl *thread);
+  void enqueue_sleep(ThreadSimpleImpl *thread, double seconds);
+  void enqueue_block(ThreadSimpleImpl *thread, BlockerSimple *blocker);
+  void unblock_one(BlockerSimple *blocker);
+  void unblock_all(BlockerSimple *blocker);
+  void enqueue_finished(ThreadSimpleImpl *thread);
+  void preempt(ThreadSimpleImpl *thread);
+  void next_context();
+
+  void prepare_for_exit();
+
+  INLINE ThreadSimpleImpl *get_current_thread();
+  void set_current_thread(ThreadSimpleImpl *current_thread);
+
+  INLINE double get_current_time() const;
+  INLINE static ThreadSimpleManager *get_global_ptr();
+
+private:
+  static void init_pointers();
+  void wake_sleepers(double now);
+  static void system_sleep(double seconds);
+  void report_deadlock();
+
+  // STL function object to sort the priority queue of sleeping threads.
+  class CompareStartTime {
+  public:
+    INLINE bool operator ()(ThreadSimpleImpl *a, ThreadSimpleImpl *b) const;
+  };
+
+  typedef pdeque<ThreadSimpleImpl *> FifoThreads;
+  typedef pvector<ThreadSimpleImpl *> Sleeping;
+
+  void kill_non_joinable(FifoThreads &threads);
+  void kill_non_joinable(Sleeping &threads);
+
+private:
+  ThreadSimpleImpl *volatile _current_thread;
+
+  // FIFO list of ready threads.
+  FifoThreads _ready;
+
+  typedef pmap<BlockerSimple *, FifoThreads> Blocked;
+  Blocked _blocked;
+
+  // Priority queue (partially-ordered heap) based on wakeup time.
+  Sleeping _sleeping;
+
+  FifoThreads _finished;
+
+  ThreadSimpleImpl *_waiting_for_exit;
+
+  TrueClock *_clock;
+
+  static bool _pointers_initialized;
+  static ThreadSimpleManager *_global_ptr;
+};
+
+// We include this down here to avoid the circularity problem.
+/* okcircular */
+#include "threadSimpleImpl.h"
+
+#include "threadSimpleManager.I"
+
+#endif // THREAD_SIMPLE_IMPL
+
+#endif

+ 28 - 0
panda/src/pipeline/threadWin32Impl.I

@@ -32,6 +32,15 @@ ThreadWin32Impl(Thread *parent_obj) :
   _status = S_new;
   _status = S_new;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadWin32Impl::preempt
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadWin32Impl::
+preempt() {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadWin32Impl::prepare_for_exit
 //     Function: ThreadWin32Impl::prepare_for_exit
 //       Access: Public
 //       Access: Public
@@ -89,3 +98,22 @@ INLINE void ThreadWin32Impl::
 sleep(double seconds) {
 sleep(double seconds) {
   Sleep((int)(seconds * 1000));
   Sleep((int)(seconds * 1000));
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadWin32Impl::yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadWin32Impl::
+yield() {
+  sleep(0.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadWin32Impl::consider_yield
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadWin32Impl::
+consider_yield() {
+}

+ 13 - 14
panda/src/pipeline/threadWin32Impl.cxx

@@ -42,6 +42,18 @@ ThreadWin32Impl::
   CloseHandle(_thread);
   CloseHandle(_thread);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadWin32Impl::setup_main_thread
+//       Access: Public
+//  Description: Called for the main thread only, which has been
+//               already started, to fill in the values appropriate to
+//               that thread.
+////////////////////////////////////////////////////////////////////
+void ThreadWin32Impl::
+setup_main_thread() {
+  _status = S_running;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadWin32Impl::start
 //     Function: ThreadWin32Impl::start
 //       Access: Public
 //       Access: Public
@@ -105,19 +117,6 @@ start(ThreadPriority priority, bool joinable) {
   return true;
   return true;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: ThreadWin32Impl::interrupt
-//       Access: Public
-//  Description: Sends an interrupt message to the thread.  This will
-//               interrupt any blocking-type system calls the thread
-//               may be waiting on, such as I/O, so that the thread
-//               may continue some other processing.  The specific
-//               behavior is implementation dependent.
-////////////////////////////////////////////////////////////////////
-void ThreadWin32Impl::
-interrupt() {
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadWin32Impl::join
 //     Function: ThreadWin32Impl::join
 //       Access: Public
 //       Access: Public
@@ -148,7 +147,7 @@ DWORD ThreadWin32Impl::
 root_func(LPVOID data) {
 root_func(LPVOID data) {
   TAU_REGISTER_THREAD();
   TAU_REGISTER_THREAD();
   {
   {
-    TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER);
+    TAU_PROFILE("void ThreadWin32Impl::root_func()", " ", TAU_USER);
 
 
     ThreadWin32Impl *self = (ThreadWin32Impl *)data;
     ThreadWin32Impl *self = (ThreadWin32Impl *)data;
     BOOL result = TlsSetValue(_pt_ptr_index, self->_parent_obj);
     BOOL result = TlsSetValue(_pt_ptr_index, self->_parent_obj);

+ 4 - 1
panda/src/pipeline/threadWin32Impl.h

@@ -40,9 +40,10 @@ public:
   INLINE ThreadWin32Impl(Thread *parent_obj);
   INLINE ThreadWin32Impl(Thread *parent_obj);
   ~ThreadWin32Impl();
   ~ThreadWin32Impl();
 
 
+  void setup_main_thread();
   bool start(ThreadPriority priority, bool joinable);
   bool start(ThreadPriority priority, bool joinable);
-  void interrupt();
   void join();
   void join();
+  INLINE void preempt();
 
 
   INLINE static void prepare_for_exit();
   INLINE static void prepare_for_exit();
 
 
@@ -50,6 +51,8 @@ public:
   INLINE static void bind_thread(Thread *thread);
   INLINE static void bind_thread(Thread *thread);
   INLINE static bool is_threading_supported();
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
   INLINE static void sleep(double seconds);
+  INLINE static void yield();
+  INLINE static void consider_yield();
 
 
 private:
 private:
   static DWORD WINAPI root_func(LPVOID data);
   static DWORD WINAPI root_func(LPVOID data);

+ 2 - 8
panda/src/putil/copyOnWritePointer.cxx

@@ -77,19 +77,13 @@ get_write_pointer() {
   Thread *current_thread = Thread::get_current_thread();
   Thread *current_thread = Thread::get_current_thread();
 
 
   MutexHolder holder(_object->_lock_mutex);
   MutexHolder holder(_object->_lock_mutex);
-  while (_object->_lock_status == CopyOnWriteObject::LS_locked_write) {
-    if (_object->_locking_thread == current_thread) {
-      return _object;
-    }
+  while (_object->_lock_status == CopyOnWriteObject::LS_locked_write &&
+         _object->_locking_thread != current_thread) {
     _object->_lock_cvar.wait();
     _object->_lock_cvar.wait();
   }
   }
 
 
   if (_object->_lock_status == CopyOnWriteObject::LS_locked_read) {
   if (_object->_lock_status == CopyOnWriteObject::LS_locked_read) {
     nassertr(_object->get_ref_count() > _object->get_cache_ref_count(), NULL);
     nassertr(_object->get_ref_count() > _object->get_cache_ref_count(), NULL);
-    if (_object->_locking_thread == current_thread) {
-      _object->_lock_status = CopyOnWriteObject::LS_locked_write;
-      return _object;
-    }
 
 
     if (util_cat.is_debug()) {
     if (util_cat.is_debug()) {
       util_cat.debug()
       util_cat.debug()