From 8d2bdc39fd8ff619fee5f4fb6608c47257f351c0 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 21 Jun 2007 15:33:27 +0000 Subject: [PATCH] first pass at SIMPLE_THREADS --- dtool/Config.pp | 44 +- dtool/LocalSetup.pp | 13 + dtool/src/dtoolbase/atomicAdjust.h | 2 +- dtool/src/dtoolbase/mutexImpl.h | 2 +- dtool/src/dtoolbase/selectThreadImpl.h | 6 + .../interfaceMakerPythonNative.cxx | 12 + dtool/src/parser-inc/Python.h | 1 + dtool/src/parser-inc/queue | 18 + dtool/src/pystub/pystub.cxx | 4 + panda/src/display/graphicsEngine.cxx | 8 +- panda/src/event/eventHandler.cxx | 2 +- panda/src/event/eventHandler.h | 2 +- panda/src/express/trueClock.I | 43 ++ panda/src/express/trueClock.cxx | 21 +- panda/src/express/trueClock.h | 7 +- panda/src/framework/pandaFramework.cxx | 2 + panda/src/gobj/vertexDataPage.cxx | 2 + panda/src/pipeline/Sources.pp | 67 ++- panda/src/pipeline/blockerSimple.I | 38 ++ panda/src/pipeline/blockerSimple.h | 57 +++ panda/src/pipeline/conditionVar.h | 2 +- panda/src/pipeline/conditionVarDummyImpl.I | 1 + panda/src/pipeline/conditionVarDummyImpl.h | 1 + panda/src/pipeline/conditionVarImpl.h | 6 + panda/src/pipeline/conditionVarSimpleImpl.I | 60 +++ panda/src/pipeline/conditionVarSimpleImpl.cxx | 65 +++ panda/src/pipeline/conditionVarSimpleImpl.h | 55 +++ panda/src/pipeline/mainThread.cxx | 1 + panda/src/pipeline/mutexDebug.I | 6 +- panda/src/pipeline/mutexDebug.cxx | 2 +- panda/src/pipeline/mutexDebug.h | 6 +- panda/src/pipeline/mutexDirect.h | 4 +- panda/src/pipeline/mutexSimpleImpl.I | 78 +++ panda/src/pipeline/mutexSimpleImpl.cxx | 56 +++ panda/src/pipeline/mutexSimpleImpl.h | 68 +++ panda/src/pipeline/mutexTrueImpl.h | 57 +++ panda/src/pipeline/pipeline_composite1.cxx | 1 + panda/src/pipeline/pipeline_composite2.cxx | 4 + panda/src/pipeline/pythonThread.cxx | 17 +- panda/src/pipeline/reMutexSimpleImpl.I | 82 ++++ panda/src/pipeline/reMutexSimpleImpl.cxx | 57 +++ panda/src/pipeline/reMutexSimpleImpl.h | 58 +++ panda/src/pipeline/test_atomic.cxx | 7 +- panda/src/pipeline/test_concurrency.cxx | 7 +- panda/src/pipeline/test_delete.cxx | 22 +- panda/src/pipeline/test_diners.cxx | 11 +- panda/src/pipeline/test_mutex.cxx | 4 +- panda/src/pipeline/test_setjmp.cxx | 44 ++ panda/src/pipeline/test_threaddata.cxx | 2 +- panda/src/pipeline/thread.I | 63 ++- panda/src/pipeline/thread.cxx | 6 +- panda/src/pipeline/thread.h | 8 +- panda/src/pipeline/threadDummyImpl.I | 57 ++- panda/src/pipeline/threadDummyImpl.h | 5 +- panda/src/pipeline/threadImpl.h | 5 + panda/src/pipeline/threadPosixImpl.I | 28 ++ panda/src/pipeline/threadPosixImpl.cxx | 25 +- panda/src/pipeline/threadPosixImpl.h | 5 +- panda/src/pipeline/threadSimpleImpl.I | 108 +++++ panda/src/pipeline/threadSimpleImpl.cxx | 273 +++++++++++ panda/src/pipeline/threadSimpleImpl.h | 151 ++++++ .../src/pipeline/threadSimpleImpl_no_opt_.cxx | 54 +++ panda/src/pipeline/threadSimpleManager.I | 64 +++ panda/src/pipeline/threadSimpleManager.cxx | 447 ++++++++++++++++++ panda/src/pipeline/threadSimpleManager.h | 121 +++++ panda/src/pipeline/threadWin32Impl.I | 28 ++ panda/src/pipeline/threadWin32Impl.cxx | 27 +- panda/src/pipeline/threadWin32Impl.h | 5 +- panda/src/putil/copyOnWritePointer.cxx | 10 +- 69 files changed, 2482 insertions(+), 143 deletions(-) create mode 100644 panda/src/pipeline/blockerSimple.I create mode 100644 panda/src/pipeline/blockerSimple.h create mode 100644 panda/src/pipeline/conditionVarSimpleImpl.I create mode 100644 panda/src/pipeline/conditionVarSimpleImpl.cxx create mode 100644 panda/src/pipeline/conditionVarSimpleImpl.h create mode 100644 panda/src/pipeline/mutexSimpleImpl.I create mode 100644 panda/src/pipeline/mutexSimpleImpl.cxx create mode 100644 panda/src/pipeline/mutexSimpleImpl.h create mode 100644 panda/src/pipeline/mutexTrueImpl.h create mode 100644 panda/src/pipeline/reMutexSimpleImpl.I create mode 100644 panda/src/pipeline/reMutexSimpleImpl.cxx create mode 100644 panda/src/pipeline/reMutexSimpleImpl.h create mode 100644 panda/src/pipeline/test_setjmp.cxx create mode 100644 panda/src/pipeline/threadSimpleImpl.I create mode 100644 panda/src/pipeline/threadSimpleImpl.cxx create mode 100644 panda/src/pipeline/threadSimpleImpl.h create mode 100644 panda/src/pipeline/threadSimpleImpl_no_opt_.cxx create mode 100644 panda/src/pipeline/threadSimpleManager.I create mode 100644 panda/src/pipeline/threadSimpleManager.cxx create mode 100644 panda/src/pipeline/threadSimpleManager.h diff --git a/dtool/Config.pp b/dtool/Config.pp index 15e2875b22..31fa44bf0d 100644 --- a/dtool/Config.pp +++ b/dtool/Config.pp @@ -678,13 +678,39 @@ // supports kernel threads running on different CPU's), but it will // slightly slow down Panda for the single CPU case, so this is not // 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 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 // thread and synchronization operations. With threading enabled, // defining this will also enable deadlock detection and logging. @@ -941,6 +967,10 @@ // (above) is set to 2, 3, or 4. #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 // value of OPTIMIZE? We separate this so we can pass these same // 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 // relevant flags. #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 // building shared (relocatable) sources? Some architectures require diff --git a/dtool/LocalSetup.pp b/dtool/LocalSetup.pp index 8302a6e799..f029248cf2 100644 --- a/dtool/LocalSetup.pp +++ b/dtool/LocalSetup.pp @@ -143,6 +143,15 @@ #else #print Configuring Panda WITHOUT Python interfaces. #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 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. */ $[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. */ $[cdefine DEBUG_THREADS] diff --git a/dtool/src/dtoolbase/atomicAdjust.h b/dtool/src/dtoolbase/atomicAdjust.h index 06eed575bd..5d19e192ba 100644 --- a/dtool/src/dtoolbase/atomicAdjust.h +++ b/dtool/src/dtoolbase/atomicAdjust.h @@ -22,7 +22,7 @@ #include "dtoolbase.h" #include "selectThreadImpl.h" -#if defined(THREAD_DUMMY_IMPL) +#if defined(THREAD_DUMMY_IMPL)||defined(THREAD_SIMPLE_IMPL) #include "atomicAdjustDummyImpl.h" typedef AtomicAdjustDummyImpl AtomicAdjust; diff --git a/dtool/src/dtoolbase/mutexImpl.h b/dtool/src/dtoolbase/mutexImpl.h index 4e4df9fb2c..aa1ee81824 100644 --- a/dtool/src/dtoolbase/mutexImpl.h +++ b/dtool/src/dtoolbase/mutexImpl.h @@ -22,7 +22,7 @@ #include "dtoolbase.h" #include "selectThreadImpl.h" -#if defined(THREAD_DUMMY_IMPL) +#if defined(THREAD_DUMMY_IMPL)||defined(THREAD_SIMPLE_IMPL) #include "mutexDummyImpl.h" typedef MutexDummyImpl MutexImpl; diff --git a/dtool/src/dtoolbase/selectThreadImpl.h b/dtool/src/dtoolbase/selectThreadImpl.h index 43c8fbd662..a4cb3a7f89 100644 --- a/dtool/src/dtoolbase/selectThreadImpl.h +++ b/dtool/src/dtoolbase/selectThreadImpl.h @@ -49,6 +49,12 @@ #undef 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) // In Windows, use the native threading library. diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 16ae93c1f4..81069c4df8 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/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 // return expression. 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) << "PyThreadState *_save;\n"; indent(out, extra_indent_level) << "Py_UNBLOCK_THREADS\n"; + out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (track_interpreter) { 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"; } if (remap->_blocking) { + out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; indent(out, extra_indent_level) << "Py_BLOCK_THREADS\n"; + out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (!extra_cleanup.empty()) { indent(out,extra_indent_level) << extra_cleanup << "\n"; @@ -2309,10 +2315,12 @@ void InterfaceMakerPythonNative::write_function_instance(ostream &out, Interface } else { if (remap->_blocking) { + out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; indent(out, extra_indent_level) << "PyThreadState *_save;\n"; indent(out, extra_indent_level) << "Py_UNBLOCK_THREADS\n"; + out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (track_interpreter) { 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"; } if (remap->_blocking) { + out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; indent(out, extra_indent_level) << "Py_BLOCK_THREADS\n"; + out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (!extra_cleanup.empty()) { 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"; } if (remap->_blocking) { + out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n"; indent(out, extra_indent_level) << "Py_BLOCK_THREADS\n"; + out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n"; } if (!extra_cleanup.empty()) { indent(out,extra_indent_level) << extra_cleanup << "\n"; diff --git a/dtool/src/parser-inc/Python.h b/dtool/src/parser-inc/Python.h index 1b5316b7f2..044d201522 100755 --- a/dtool/src/parser-inc/Python.h +++ b/dtool/src/parser-inc/Python.h @@ -25,5 +25,6 @@ #define PYTHON_H class PyObject; +class PyThreadState; #endif // PYTHON_H diff --git a/dtool/src/parser-inc/queue b/dtool/src/parser-inc/queue index 5d9f1dd63b..bd39f7eec5 100644 --- a/dtool/src/parser-inc/queue +++ b/dtool/src/parser-inc/queue @@ -44,4 +44,22 @@ public: class difference_type; }; +template +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 diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 36313f421b..6eab413835 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -85,6 +85,8 @@ extern "C" { EXPCL_DTOOLCONFIG int PyString_FromString(...); EXPCL_DTOOLCONFIG int PyString_FromStringAndSize(...); EXPCL_DTOOLCONFIG int PyString_Type(...); + EXPCL_DTOOLCONFIG int PyThreadState_Get(...); + EXPCL_DTOOLCONFIG int PyThreadState_Swap(...); EXPCL_DTOOLCONFIG int PyTuple_GetItem(...); EXPCL_DTOOLCONFIG int PyTuple_New(...); EXPCL_DTOOLCONFIG int PyTuple_Size(...); @@ -176,6 +178,8 @@ int PyString_AsStringAndSize(...) { return 0; } int PyString_FromString(...) { return 0; } int PyString_FromStringAndSize(...) { return 0; } int PyString_Type(...) { return 0; } +int PyThreadState_Get(...) { return 0; } +int PyThreadState_Swap(...) { return 0; } int PyTuple_GetItem(...) { return 0; } int PyTuple_New(...) { return 0; } int PyTuple_Size(...) { return 0; }; diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 232cc296f6..3dde417c4d 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -506,6 +506,9 @@ remove_all_windows() { // And, hey, let's stop the vertex paging thread, if any. 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 // running applications. 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() diff --git a/panda/src/event/eventHandler.cxx b/panda/src/event/eventHandler.cxx index 338b43fb68..b15c52b0bd 100644 --- a/panda/src/event/eventHandler.cxx +++ b/panda/src/event/eventHandler.cxx @@ -31,7 +31,7 @@ EventHandler *EventHandler::_global_event_handler = 0; // Description: //////////////////////////////////////////////////////////////////// EventHandler:: -EventHandler(EventQueue *queue) : _queue(*queue) { +EventHandler(EventQueue *ev_queue) : _queue(*ev_queue) { } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/event/eventHandler.h b/panda/src/event/eventHandler.h index 83f3d18d9a..b1aa923a0f 100644 --- a/panda/src/event/eventHandler.h +++ b/panda/src/event/eventHandler.h @@ -49,7 +49,7 @@ public: typedef void EventCallbackFunction(const Event *, void *); PUBLISHED: - EventHandler(EventQueue *queue); + EventHandler(EventQueue *ev_queue); void process_events(); diff --git a/panda/src/express/trueClock.I b/panda/src/express/trueClock.I index be206acf79..21b734aa62 100644 --- a/panda/src/express/trueClock.I +++ b/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 // Access: Published diff --git a/panda/src/express/trueClock.cxx b/panda/src/express/trueClock.cxx index 8aff49b82f..1a2e916e45 100644 --- a/panda/src/express/trueClock.cxx +++ b/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 // Description: //////////////////////////////////////////////////////////////////// double TrueClock:: -get_short_time() { +get_short_raw_time() { double time; - if (_paranoid_clock) { - _lock.lock(); - } - if (_has_high_res) { // Use the high-resolution clock. This is of questionable value, // since (a) on some OS's and hardware, the low 24 bits can @@ -122,15 +118,6 @@ get_short_time() { int tc = GetTickCount(); 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 // Description: //////////////////////////////////////////////////////////////////// double TrueClock:: -get_short_time() { +get_short_raw_time() { struct timeval tv; int result; diff --git a/panda/src/express/trueClock.h b/panda/src/express/trueClock.h index cbb98eeec7..b4574ba83f 100644 --- a/panda/src/express/trueClock.h +++ b/panda/src/express/trueClock.h @@ -50,7 +50,12 @@ PUBLISHED: // short interval. It may tend to drift over the long haul, but it // should have lots of digits to measure short intervals very // 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; diff --git a/panda/src/framework/pandaFramework.cxx b/panda/src/framework/pandaFramework.cxx index e4bad7b6be..8035a09a31 100644 --- a/panda/src/framework/pandaFramework.cxx +++ b/panda/src/framework/pandaFramework.cxx @@ -146,6 +146,8 @@ close_framework() { _exit_flag = false; _recorder = NULL; + + Thread::prepare_for_exit(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/vertexDataPage.cxx b/panda/src/gobj/vertexDataPage.cxx index 067b83a7a9..d9ef00e957 100644 --- a/panda/src/gobj/vertexDataPage.cxx +++ b/panda/src/gobj/vertexDataPage.cxx @@ -744,6 +744,8 @@ thread_main() { _working_page = NULL; _working_cvar.signal(); + + Thread::consider_yield(); } } diff --git a/panda/src/pipeline/Sources.pp b/panda/src/pipeline/Sources.pp index 2ce9eaef55..f8ec79e9c5 100644 --- a/panda/src/pipeline/Sources.pp +++ b/panda/src/pipeline/Sources.pp @@ -11,6 +11,8 @@ #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx #define SOURCES \ + ThreadSimpleImpl_no_opt_.cxx \ + blockerSimple.h blockerSimple.I \ conditionVar.h conditionVar.I \ conditionVarDebug.h conditionVarDebug.I \ conditionVarDirect.h conditionVarDirect.I \ @@ -22,6 +24,7 @@ conditionVarImpl.h \ conditionVarPosixImpl.h conditionVarPosixImpl.I \ conditionVarWin32Impl.h conditionVarWin32Impl.I \ + conditionVarSimpleImpl.h conditionVarSimpleImpl.I \ conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \ config_pipeline.h \ cycleData.h cycleData.I \ @@ -37,6 +40,8 @@ mutexDebug.h mutexDebug.I \ mutexDirect.h mutexDirect.I \ mutexHolder.h mutexHolder.I \ + mutexSimpleImpl.h mutexSimpleImpl.I \ + mutexTrueImpl.h \ pipeline.h pipeline.I \ pipelineCycler.h pipelineCycler.I \ pipelineCyclerLinks.h pipelineCyclerLinks.I \ @@ -49,8 +54,12 @@ reMutex.I reMutex.h \ reMutexDirect.h reMutexDirect.I \ 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 \ + threadSimpleImpl.h threadSimpleImpl.I \ + threadSimpleManager.h threadSimpleManager.I \ threadWin32Impl.h threadWin32Impl.I \ threadPriority.h @@ -65,6 +74,7 @@ conditionVarFullWin32Impl.cxx \ conditionVarPosixImpl.cxx \ conditionVarWin32Impl.cxx \ + conditionVarSimpleImpl.cxx \ conditionVarSpinlockImpl.cxx \ config_pipeline.cxx \ cycleData.cxx \ @@ -80,6 +90,7 @@ mutexDebug.cxx \ mutexDirect.cxx \ mutexHolder.cxx \ + mutexSimpleImpl.cxx \ pipeline.cxx \ pipelineCycler.cxx \ pipelineCyclerDummyImpl.cxx \ @@ -90,11 +101,16 @@ reMutex.cxx \ reMutexDirect.cxx \ reMutexHolder.cxx \ - thread.cxx threadDummyImpl.cxx \ + reMutexSimpleImpl.cxx \ + thread.cxx \ + threadDummyImpl.cxx \ threadPosixImpl.cxx \ + threadSimpleImpl.cxx \ + threadSimpleManager.cxx \ threadWin32Impl.cxx #define INSTALL_HEADERS \ + blockerSimple.h blockerSimple.I \ conditionVar.h conditionVar.I \ conditionVarDebug.h conditionVarDebug.I \ conditionVarDirect.h conditionVarDirect.I \ @@ -106,6 +122,7 @@ conditionVarImpl.h \ conditionVarPosixImpl.h conditionVarPosixImpl.I \ conditionVarWin32Impl.h conditionVarWin32Impl.I \ + conditionVarSimpleImpl.h conditionVarSimpleImpl.I \ conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \ config_pipeline.h \ cycleData.h cycleData.I \ @@ -121,6 +138,8 @@ mutexDebug.h mutexDebug.I \ mutexDirect.h mutexDirect.I \ mutexHolder.h mutexHolder.I \ + mutexSimpleImpl.h mutexSimpleImpl.I \ + mutexTrueImpl.h \ pipeline.h pipeline.I \ pipelineCycler.h pipelineCycler.I \ pipelineCyclerLinks.h pipelineCyclerLinks.I \ @@ -133,8 +152,12 @@ reMutex.I reMutex.h \ reMutexDirect.h reMutexDirect.I \ 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 \ + threadSimpleImpl.h threadSimpleImpl.I \ + threadSimpleManager.h threadSimpleManager.I \ threadWin32Impl.h threadWin32Impl.I \ threadPriority.h @@ -146,7 +169,9 @@ #begin test_bin_target #define TARGET test_threaddata #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 \ test_threaddata.cxx @@ -157,7 +182,9 @@ #begin test_bin_target #define TARGET test_diners #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 \ test_diners.cxx @@ -168,7 +195,9 @@ #begin test_bin_target #define TARGET test_mutex #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 \ test_mutex.cxx @@ -179,7 +208,9 @@ #begin test_bin_target #define TARGET test_concurrency #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 \ test_concurrency.cxx @@ -190,7 +221,9 @@ #begin test_bin_target #define TARGET test_delete #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 \ test_delete.cxx @@ -201,10 +234,26 @@ #begin test_bin_target #define TARGET test_atomic #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 \ test_atomic.cxx #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 + diff --git a/panda/src/pipeline/blockerSimple.I b/panda/src/pipeline/blockerSimple.I new file mode 100644 index 0000000000..63a512ea96 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: BlockerSimple::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BlockerSimple:: +BlockerSimple() { + _flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: BlockerSimple::Destructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BlockerSimple:: +~BlockerSimple() { + nassertv(_flags == 0); +} diff --git a/panda/src/pipeline/blockerSimple.h b/panda/src/pipeline/blockerSimple.h new file mode 100644 index 0000000000..56ea319ec0 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/conditionVar.h b/panda/src/pipeline/conditionVar.h index 8d6948883f..7fac4e5d6d 100644 --- a/panda/src/pipeline/conditionVar.h +++ b/panda/src/pipeline/conditionVar.h @@ -30,7 +30,7 @@ // waiting for something to happen. A condition // variable can be used to "wake up" a thread when some // arbitrary condition has changed. - +// // The ConditionVar class does not support the full // semantics of POSIX condition variables. In // particular, it does not support the broadcast or diff --git a/panda/src/pipeline/conditionVarDummyImpl.I b/panda/src/pipeline/conditionVarDummyImpl.I index 843b999699..54104d1a36 100644 --- a/panda/src/pipeline/conditionVarDummyImpl.I +++ b/panda/src/pipeline/conditionVarDummyImpl.I @@ -42,6 +42,7 @@ INLINE ConditionVarDummyImpl:: //////////////////////////////////////////////////////////////////// INLINE void ConditionVarDummyImpl:: wait() { + Thread::force_yield(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pipeline/conditionVarDummyImpl.h b/panda/src/pipeline/conditionVarDummyImpl.h index f0ac838c7c..bde3c3f735 100644 --- a/panda/src/pipeline/conditionVarDummyImpl.h +++ b/panda/src/pipeline/conditionVarDummyImpl.h @@ -21,6 +21,7 @@ #include "pandabase.h" #include "selectThreadImpl.h" +#include "thread.h" #include "pnotify.h" diff --git a/panda/src/pipeline/conditionVarImpl.h b/panda/src/pipeline/conditionVarImpl.h index 618be73fda..0094010c00 100644 --- a/panda/src/pipeline/conditionVarImpl.h +++ b/panda/src/pipeline/conditionVarImpl.h @@ -28,6 +28,12 @@ typedef ConditionVarDummyImpl ConditionVarImpl; typedef ConditionVarDummyImpl ConditionVarFullImpl; +#elif defined(THREAD_SIMPLE_IMPL) + +#include "conditionVarSimpleImpl.h" +typedef ConditionVarSimpleImpl ConditionVarImpl; +typedef ConditionVarSimpleImpl ConditionVarFullImpl; + #elif defined(MUTEX_SPINLOCK) #include "conditionVarSpinlockImpl.h" diff --git a/panda/src/pipeline/conditionVarSimpleImpl.I b/panda/src/pipeline/conditionVarSimpleImpl.I new file mode 100644 index 0000000000..0eed062eb6 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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(); + } +} diff --git a/panda/src/pipeline/conditionVarSimpleImpl.cxx b/panda/src/pipeline/conditionVarSimpleImpl.cxx new file mode 100644 index 0000000000..71b859c442 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/conditionVarSimpleImpl.h b/panda/src/pipeline/conditionVarSimpleImpl.h new file mode 100644 index 0000000000..ecc53bcba2 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/mainThread.cxx b/panda/src/pipeline/mainThread.cxx index e558c0cf77..aa4a53425b 100644 --- a/panda/src/pipeline/mainThread.cxx +++ b/panda/src/pipeline/mainThread.cxx @@ -28,6 +28,7 @@ TypeHandle MainThread::_type_handle; MainThread:: MainThread() : Thread("Main", "Main") { init_type(); // in case static init comes in the wrong order + _impl.setup_main_thread(); _started = true; } diff --git a/panda/src/pipeline/mutexDebug.I b/panda/src/pipeline/mutexDebug.I index 9e8caa3912..4636ed8aa8 100755 --- a/panda/src/pipeline/mutexDebug.I +++ b/panda/src/pipeline/mutexDebug.I @@ -151,10 +151,10 @@ debug_is_locked() const { // (non-static) methods of MutexDebug may simply assume // that the pointer has already been created. //////////////////////////////////////////////////////////////////// -INLINE MutexImpl *MutexDebug:: +INLINE MutexTrueImpl *MutexDebug:: get_global_lock() { - if (_global_lock == (MutexImpl *)NULL) { - _global_lock = new MutexImpl; + if (_global_lock == (MutexTrueImpl *)NULL) { + _global_lock = new MutexTrueImpl; } return _global_lock; } diff --git a/panda/src/pipeline/mutexDebug.cxx b/panda/src/pipeline/mutexDebug.cxx index 09b6ca3a15..d0666eaa56 100755 --- a/panda/src/pipeline/mutexDebug.cxx +++ b/panda/src/pipeline/mutexDebug.cxx @@ -22,7 +22,7 @@ #ifdef DEBUG_THREADS -MutexImpl *MutexDebug::_global_lock; +MutexTrueImpl *MutexDebug::_global_lock; //////////////////////////////////////////////////////////////////// // Function: MutexDebug::Constructor diff --git a/panda/src/pipeline/mutexDebug.h b/panda/src/pipeline/mutexDebug.h index 41e26fce88..4fc722e08e 100644 --- a/panda/src/pipeline/mutexDebug.h +++ b/panda/src/pipeline/mutexDebug.h @@ -20,7 +20,7 @@ #define MUTEXDEBUG_H #include "pandabase.h" -#include "mutexImpl.h" +#include "mutexTrueImpl.h" #include "conditionVarImpl.h" #include "thread.h" @@ -59,7 +59,7 @@ private: void report_deadlock(Thread *this_thread); private: - INLINE static MutexImpl *get_global_lock(); + INLINE static MutexTrueImpl *get_global_lock(); string _name; bool _allow_recursion; @@ -68,7 +68,7 @@ private: ConditionVarImpl _cvar_impl; - static MutexImpl *_global_lock; + static MutexTrueImpl *_global_lock; friend class ConditionVarDebug; friend class ConditionVarFullDebug; diff --git a/panda/src/pipeline/mutexDirect.h b/panda/src/pipeline/mutexDirect.h index 5e1087700f..4a709a43e7 100644 --- a/panda/src/pipeline/mutexDirect.h +++ b/panda/src/pipeline/mutexDirect.h @@ -20,7 +20,7 @@ #define MUTEXDIRECT_H #include "pandabase.h" -#include "mutexImpl.h" +#include "mutexTrueImpl.h" #include "pnotify.h" class Thread; @@ -49,7 +49,7 @@ PUBLISHED: void output(ostream &out) const; private: - MutexImpl _impl; + MutexTrueImpl _impl; friend class ConditionVarDirect; friend class ConditionVarFullDirect; diff --git a/panda/src/pipeline/mutexSimpleImpl.I b/panda/src/pipeline/mutexSimpleImpl.I new file mode 100644 index 0000000000..f6e9a97e7f --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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(); + } +} diff --git a/panda/src/pipeline/mutexSimpleImpl.cxx b/panda/src/pipeline/mutexSimpleImpl.cxx new file mode 100644 index 0000000000..2113a6d6fe --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/mutexSimpleImpl.h b/panda/src/pipeline/mutexSimpleImpl.h new file mode 100644 index 0000000000..cc1225d34b --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/mutexTrueImpl.h b/panda/src/pipeline/mutexTrueImpl.h new file mode 100644 index 0000000000..a2197d1bc3 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + + + diff --git a/panda/src/pipeline/pipeline_composite1.cxx b/panda/src/pipeline/pipeline_composite1.cxx index e0e3e353b6..4c16385f6f 100644 --- a/panda/src/pipeline/pipeline_composite1.cxx +++ b/panda/src/pipeline/pipeline_composite1.cxx @@ -7,6 +7,7 @@ #include "conditionVarFullDirect.cxx" #include "conditionVarPosixImpl.cxx" #include "conditionVarWin32Impl.cxx" +#include "conditionVarSimpleImpl.cxx" #include "conditionVarSpinlockImpl.cxx" #include "config_pipeline.cxx" #include "cycleData.cxx" diff --git a/panda/src/pipeline/pipeline_composite2.cxx b/panda/src/pipeline/pipeline_composite2.cxx index 8833e6fa6d..149406c9b3 100644 --- a/panda/src/pipeline/pipeline_composite2.cxx +++ b/panda/src/pipeline/pipeline_composite2.cxx @@ -3,6 +3,7 @@ #include "mutexDebug.cxx" #include "mutexDirect.cxx" #include "mutexHolder.cxx" +#include "mutexSimpleImpl.cxx" #include "pipeline.cxx" #include "pipelineCycler.cxx" #include "pipelineCyclerDummyImpl.cxx" @@ -13,7 +14,10 @@ #include "reMutex.cxx" #include "reMutexDirect.cxx" #include "reMutexHolder.cxx" +#include "reMutexSimpleImpl.cxx" #include "thread.cxx" #include "threadDummyImpl.cxx" #include "threadPosixImpl.cxx" +#include "threadSimpleImpl.cxx" +#include "threadSimpleManager.cxx" #include "threadWin32Impl.cxx" diff --git a/panda/src/pipeline/pythonThread.cxx b/panda/src/pipeline/pythonThread.cxx index 92c92c0d7d..12b7d4eadf 100644 --- a/panda/src/pipeline/pythonThread.cxx +++ b/panda/src/pipeline/pythonThread.cxx @@ -132,16 +132,16 @@ handle_python_exception() { ostringstream strm; strm << "\n"; - PyObject *exc_name = PyObject_GetAttrString(exc, "__name__"); - if (exc_name == (PyObject *)NULL) { - PyObject *exc_str = PyObject_Str(exc); - strm << PyString_AsString(exc_str); - Py_DECREF(exc_str); - } else { + if (PyObject_HasAttrString(exc, "__name__")) { + PyObject *exc_name = PyObject_GetAttrString(exc, "__name__"); PyObject *exc_str = PyObject_Str(exc_name); strm << PyString_AsString(exc_str); Py_DECREF(exc_str); Py_DECREF(exc_name); + } else { + PyObject *exc_str = PyObject_Str(exc); + strm << PyString_AsString(exc_str); + Py_DECREF(exc_str); } Py_DECREF(exc); @@ -160,6 +160,11 @@ handle_python_exception() { nout << message << "\n"; 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 diff --git a/panda/src/pipeline/reMutexSimpleImpl.I b/panda/src/pipeline/reMutexSimpleImpl.I new file mode 100644 index 0000000000..5184e3b94f --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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(); + } +} diff --git a/panda/src/pipeline/reMutexSimpleImpl.cxx b/panda/src/pipeline/reMutexSimpleImpl.cxx new file mode 100644 index 0000000000..18aab30c5a --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/reMutexSimpleImpl.h b/panda/src/pipeline/reMutexSimpleImpl.h new file mode 100644 index 0000000000..a1fd8c46c9 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/test_atomic.cxx b/panda/src/pipeline/test_atomic.cxx index 4c41c5c1dd..14c416be63 100755 --- a/panda/src/pipeline/test_atomic.cxx +++ b/panda/src/pipeline/test_atomic.cxx @@ -73,13 +73,13 @@ main(int argc, char *argv[]) { PT(MyThread) thread = new MyThread("a"); threads.push_back(thread); - thread->start(TP_normal, true, true); + thread->start(TP_normal, true); for (int i = 1; i < number_of_threads; ++i) { char name = 'a' + i; PT(MyThread) thread = new MyThread(string(1, name)); threads.push_back(thread); - thread->start(TP_normal, true, true); + thread->start(TP_normal, true); } // Now join all the threads. @@ -93,5 +93,6 @@ main(int argc, char *argv[]) { << "net_count = " << _net_count << "\n" << "num_net_count_incremented = " << _num_net_count_incremented << "\n"; - exit(0); + Thread::prepare_for_exit(); + return (0); } diff --git a/panda/src/pipeline/test_concurrency.cxx b/panda/src/pipeline/test_concurrency.cxx index e4ccbee79b..3ca6ea3fde 100755 --- a/panda/src/pipeline/test_concurrency.cxx +++ b/panda/src/pipeline/test_concurrency.cxx @@ -101,14 +101,14 @@ main(int argc, char *argv[]) { PT(MyThread) thread = new MyThread("a", 0); threads.push_back(thread); - thread->start(TP_normal, true, true); + thread->start(TP_normal, true); for (int i = 1; i < number_of_threads; ++i) { char name = 'a' + i; Thread::sleep(delay_between_threads); PT(MyThread) thread = new MyThread(string(1, name), i); threads.push_back(thread); - thread->start(TP_normal, true, true); + thread->start(TP_normal, true); } // Now join all the threads. @@ -117,5 +117,6 @@ main(int argc, char *argv[]) { (*ti)->join(); } - exit(0); + Thread::prepare_for_exit(); + return 0; } diff --git a/panda/src/pipeline/test_delete.cxx b/panda/src/pipeline/test_delete.cxx index 8b117818ef..5e9fcf8071 100644 --- a/panda/src/pipeline/test_delete.cxx +++ b/panda/src/pipeline/test_delete.cxx @@ -66,9 +66,22 @@ public: ALLOC_DELETED_CHAIN(Doober); 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 Doobers; +TypeHandle Doober::_type_handle; + class MyThread : public Thread { public: @@ -116,6 +129,8 @@ public: int main(int argc, char *argv[]) { + Doober::init_type(); + OUTPUT(nout << "Making " << number_of_threads << " threads.\n"); typedef pvector< PT(MyThread) > Threads; @@ -123,13 +138,13 @@ main(int argc, char *argv[]) { PT(MyThread) thread = new MyThread("a"); threads.push_back(thread); - thread->start(TP_normal, true, true); + thread->start(TP_normal, true); for (int i = 1; i < number_of_threads; ++i) { char name = 'a' + i; PT(MyThread) thread = new MyThread(string(1, name)); threads.push_back(thread); - thread->start(TP_normal, true, true); + thread->start(TP_normal, true); } // Now join all the threads. @@ -138,5 +153,6 @@ main(int argc, char *argv[]) { (*ti)->join(); } - exit(0); + Thread::prepare_for_exit(); + return (0); } diff --git a/panda/src/pipeline/test_diners.cxx b/panda/src/pipeline/test_diners.cxx index 89c55cbfdd..f7bb634cfc 100644 --- a/panda/src/pipeline/test_diners.cxx +++ b/panda/src/pipeline/test_diners.cxx @@ -113,6 +113,7 @@ private: } room_mutex.lock(); --room_occupancy; + cerr << "clearing philosopher " << _id << "\n"; phils[_id] = (philosopher*)0L; room_condition.signal(); room_mutex.release(); @@ -150,7 +151,7 @@ main(int argc, char *argv[]) { } for (i=0; istart(TP_normal, false, false); + phils[i]->start(TP_normal, false); } room_occupancy = N_DINERS; @@ -159,10 +160,12 @@ main(int argc, char *argv[]) { double end_time = start_time + run_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 << "\n"); - room_condition.wait(); + while (room_occupancy == N_DINERS) { + room_condition.wait(); + } } // hmm.. someone left the room. room_mutex.release(); @@ -176,7 +179,7 @@ main(int argc, char *argv[]) { break; assert(i != N_DINERS); phils[i] = new philosopher(i); - phils[i]->start(TP_normal, false, false); + phils[i]->start(TP_normal, false); ++room_occupancy; } diff --git a/panda/src/pipeline/test_mutex.cxx b/panda/src/pipeline/test_mutex.cxx index e2b813c886..ef7f044ae3 100644 --- a/panda/src/pipeline/test_mutex.cxx +++ b/panda/src/pipeline/test_mutex.cxx @@ -60,8 +60,8 @@ main(int argc, char *argv[]) { MyThread *b = new MyThread("b", _m1, 0.9); 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(); b->join(); diff --git a/panda/src/pipeline/test_setjmp.cxx b/panda/src/pipeline/test_setjmp.cxx new file mode 100644 index 0000000000..f79ebd661a --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "pandabase.h" + +#include + +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; +} diff --git a/panda/src/pipeline/test_threaddata.cxx b/panda/src/pipeline/test_threaddata.cxx index efa0e6ea9c..e9981b0127 100644 --- a/panda/src/pipeline/test_threaddata.cxx +++ b/panda/src/pipeline/test_threaddata.cxx @@ -67,7 +67,7 @@ main() { for (int i = 0; i < 10; i++) { string name = string("thread_") + (char)(i + 'a'); PT(Thread) thread = new ThreadWithData(name, i); - if (!thread->start(TP_low, false, false)) { + if (!thread->start(TP_low, true)) { MutexHolder holder(cout_mutex); cout << "Unable to start " << name << ".\n"; } else { diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I index 5373e9f6dd..e598675013 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -252,9 +252,36 @@ sleep(double 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 -// Access: Public +// Access: Published // Description: Returns true if the thread has been started, false if // it has not, or if join() has already been called. //////////////////////////////////////////////////////////////////// @@ -263,24 +290,9 @@ is_started() const { 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 -// Access: Public +// Access: Published // Description: Blocks the calling process until the thread // terminates. If the thread has already terminated, // 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 -// Access: Public +// Access: Published // Description: Should be called by the main thread just before // exiting the program, this blocks until any remaining // thread cleanup has finished. diff --git a/panda/src/pipeline/thread.cxx b/panda/src/pipeline/thread.cxx index 02cd20a5d8..c02a594c43 100644 --- a/panda/src/pipeline/thread.cxx +++ b/panda/src/pipeline/thread.cxx @@ -166,7 +166,11 @@ start(ThreadPriority priority, bool joinable) { //////////////////////////////////////////////////////////////////// void 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->ref(); } diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index ddcca540a5..79cc10b53c 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -77,15 +77,17 @@ PUBLISHED: INLINE static bool is_threading_supported(); 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; INLINE bool is_started() const; bool start(ThreadPriority priority, bool joinable); - INLINE void interrupt(); BLOCKING INLINE void join(); + INLINE void preempt(); -public: INLINE static void prepare_for_exit(); private: @@ -132,6 +134,8 @@ private: friend class ThreadDummyImpl; friend class ThreadWin32Impl; friend class ThreadPosixImpl; + friend class ThreadSimpleImpl; + friend class MainThread; }; INLINE ostream &operator << (ostream &out, const Thread &thread); diff --git a/panda/src/pipeline/threadDummyImpl.I b/panda/src/pipeline/threadDummyImpl.I index 329f5f2846..badbd2fc3a 100644 --- a/panda/src/pipeline/threadDummyImpl.I +++ b/panda/src/pipeline/threadDummyImpl.I @@ -35,6 +35,17 @@ INLINE 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 // Access: Public @@ -45,15 +56,6 @@ start(ThreadPriority, bool) { return false; } -//////////////////////////////////////////////////////////////////// -// Function: ThreadDummyImpl::interrupt -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE void ThreadDummyImpl:: -interrupt() { -} - //////////////////////////////////////////////////////////////////// // Function: ThreadDummyImpl::join // Access: Public @@ -63,6 +65,15 @@ INLINE void ThreadDummyImpl:: join() { } +//////////////////////////////////////////////////////////////////// +// Function: ThreadDummyImpl::preempt +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ThreadDummyImpl:: +preempt() { +} + //////////////////////////////////////////////////////////////////// // Function: ThreadDummyImpl::prepare_for_exit // Access: Public @@ -101,5 +112,31 @@ is_threading_supported() { // Description: //////////////////////////////////////////////////////////////////// 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() { } diff --git a/panda/src/pipeline/threadDummyImpl.h b/panda/src/pipeline/threadDummyImpl.h index c32ddfbe34..3084d2651e 100644 --- a/panda/src/pipeline/threadDummyImpl.h +++ b/panda/src/pipeline/threadDummyImpl.h @@ -40,9 +40,10 @@ public: INLINE ThreadDummyImpl(Thread *parent_obj); INLINE ~ThreadDummyImpl(); + INLINE void setup_main_thread(); INLINE bool start(ThreadPriority priority, bool joinable); - INLINE void interrupt(); INLINE void join(); + INLINE void preempt(); INLINE static void prepare_for_exit(); @@ -50,6 +51,8 @@ public: 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(); }; #include "threadDummyImpl.I" diff --git a/panda/src/pipeline/threadImpl.h b/panda/src/pipeline/threadImpl.h index 7df0565e4a..0e4869ecbf 100644 --- a/panda/src/pipeline/threadImpl.h +++ b/panda/src/pipeline/threadImpl.h @@ -27,6 +27,11 @@ #include "threadDummyImpl.h" typedef ThreadDummyImpl ThreadImpl; +#elif defined(THREAD_SIMPLE_IMPL) + +#include "threadSimpleImpl.h" +typedef ThreadSimpleImpl ThreadImpl; + #elif defined(THREAD_WIN32_IMPL) #include "threadWin32Impl.h" diff --git a/panda/src/pipeline/threadPosixImpl.I b/panda/src/pipeline/threadPosixImpl.I index 70fa1ce4d9..bff32a60fc 100644 --- a/panda/src/pipeline/threadPosixImpl.I +++ b/panda/src/pipeline/threadPosixImpl.I @@ -31,6 +31,15 @@ ThreadPosixImpl(Thread *parent_obj) : _status = S_new; } +//////////////////////////////////////////////////////////////////// +// Function: ThreadPosixImpl::preempt +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ThreadPosixImpl:: +preempt() { +} + //////////////////////////////////////////////////////////////////// // Function: ThreadPosixImpl::prepare_for_exit // Access: Public @@ -93,3 +102,22 @@ sleep(double seconds) { rqtp.tv_nsec = long((seconds - (double)rqtp.tv_sec) * 1000000000.0); 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() { +} diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index d376e0ebb1..be6a189a1b 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -51,6 +51,18 @@ ThreadPosixImpl:: _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 // Access: Public @@ -151,19 +163,6 @@ start(ThreadPriority priority, bool joinable) { 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 // Access: Public diff --git a/panda/src/pipeline/threadPosixImpl.h b/panda/src/pipeline/threadPosixImpl.h index 34c0de5a50..b6e991146e 100644 --- a/panda/src/pipeline/threadPosixImpl.h +++ b/panda/src/pipeline/threadPosixImpl.h @@ -41,9 +41,10 @@ public: INLINE ThreadPosixImpl(Thread *parent_obj); ~ThreadPosixImpl(); + void setup_main_thread(); bool start(ThreadPriority priority, bool joinable); - void interrupt(); void join(); + INLINE void preempt(); INLINE static void prepare_for_exit(); @@ -51,6 +52,8 @@ public: 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(); private: static void *root_func(void *data); diff --git a/panda/src/pipeline/threadSimpleImpl.I b/panda/src/pipeline/threadSimpleImpl.I new file mode 100644 index 0000000000..4bdc2526b9 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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; +} diff --git a/panda/src/pipeline/threadSimpleImpl.cxx b/panda/src/pipeline/threadSimpleImpl.cxx new file mode 100644 index 0000000000..4d038b887d --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/threadSimpleImpl.h b/panda/src/pipeline/threadSimpleImpl.h new file mode 100644 index 0000000000..e955337f69 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + +#endif // HAVE_PYTHON + +#include + +#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 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 diff --git a/panda/src/pipeline/threadSimpleImpl_no_opt_.cxx b/panda/src/pipeline/threadSimpleImpl_no_opt_.cxx new file mode 100644 index 0000000000..14596bcf44 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/pipeline/threadSimpleManager.I b/panda/src/pipeline/threadSimpleManager.I new file mode 100644 index 0000000000..ddd58dd51e --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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(); +} diff --git a/panda/src/pipeline/threadSimpleManager.cxx b/panda/src/pipeline/threadSimpleManager.cxx new file mode 100644 index 0000000000..960ad8b611 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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); +} diff --git a/panda/src/pipeline/threadSimpleManager.h b/panda/src/pipeline/threadSimpleManager.h new file mode 100644 index 0000000000..365c3cbc55 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + +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 FifoThreads; + typedef pvector 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 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 diff --git a/panda/src/pipeline/threadWin32Impl.I b/panda/src/pipeline/threadWin32Impl.I index 2f4bf6ad13..6dc44e0292 100644 --- a/panda/src/pipeline/threadWin32Impl.I +++ b/panda/src/pipeline/threadWin32Impl.I @@ -32,6 +32,15 @@ ThreadWin32Impl(Thread *parent_obj) : _status = S_new; } +//////////////////////////////////////////////////////////////////// +// Function: ThreadWin32Impl::preempt +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ThreadWin32Impl:: +preempt() { +} + //////////////////////////////////////////////////////////////////// // Function: ThreadWin32Impl::prepare_for_exit // Access: Public @@ -89,3 +98,22 @@ INLINE void ThreadWin32Impl:: sleep(double seconds) { 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() { +} diff --git a/panda/src/pipeline/threadWin32Impl.cxx b/panda/src/pipeline/threadWin32Impl.cxx index 6283d3f616..70177c0e52 100644 --- a/panda/src/pipeline/threadWin32Impl.cxx +++ b/panda/src/pipeline/threadWin32Impl.cxx @@ -42,6 +42,18 @@ ThreadWin32Impl:: 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 // Access: Public @@ -105,19 +117,6 @@ start(ThreadPriority priority, bool joinable) { 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 // Access: Public @@ -148,7 +147,7 @@ DWORD ThreadWin32Impl:: root_func(LPVOID data) { TAU_REGISTER_THREAD(); { - TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER); + TAU_PROFILE("void ThreadWin32Impl::root_func()", " ", TAU_USER); ThreadWin32Impl *self = (ThreadWin32Impl *)data; BOOL result = TlsSetValue(_pt_ptr_index, self->_parent_obj); diff --git a/panda/src/pipeline/threadWin32Impl.h b/panda/src/pipeline/threadWin32Impl.h index 03ac7a5e77..1168da83bf 100644 --- a/panda/src/pipeline/threadWin32Impl.h +++ b/panda/src/pipeline/threadWin32Impl.h @@ -40,9 +40,10 @@ public: INLINE ThreadWin32Impl(Thread *parent_obj); ~ThreadWin32Impl(); + void setup_main_thread(); bool start(ThreadPriority priority, bool joinable); - void interrupt(); void join(); + INLINE void preempt(); INLINE static void prepare_for_exit(); @@ -50,6 +51,8 @@ public: 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(); private: static DWORD WINAPI root_func(LPVOID data); diff --git a/panda/src/putil/copyOnWritePointer.cxx b/panda/src/putil/copyOnWritePointer.cxx index 9b231b8d91..e6980d6d90 100644 --- a/panda/src/putil/copyOnWritePointer.cxx +++ b/panda/src/putil/copyOnWritePointer.cxx @@ -77,19 +77,13 @@ get_write_pointer() { Thread *current_thread = Thread::get_current_thread(); 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(); } if (_object->_lock_status == CopyOnWriteObject::LS_locked_read) { 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()) { util_cat.debug()