first pass at SIMPLE_THREADS
This commit is contained in:
parent
cd833ba93b
commit
8d2bdc39fd
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -25,5 +25,6 @@
|
|||
#define PYTHON_H
|
||||
|
||||
class PyObject;
|
||||
class PyThreadState;
|
||||
|
||||
#endif // PYTHON_H
|
||||
|
|
|
|||
|
|
@ -44,4 +44,22 @@ public:
|
|||
class difference_type;
|
||||
};
|
||||
|
||||
template<class element>
|
||||
class priority_queue {
|
||||
public:
|
||||
typedef element value_type;
|
||||
|
||||
typedef element *pointer;
|
||||
typedef const element *const_pointer;
|
||||
typedef element &reference;
|
||||
typedef const element &const_reference;
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
class reverse_iterator;
|
||||
class const_reverse_iterator;
|
||||
typedef size_t size_type;
|
||||
class difference_type;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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; };
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ EventHandler *EventHandler::_global_event_handler = 0;
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EventHandler::
|
||||
EventHandler(EventQueue *queue) : _queue(*queue) {
|
||||
EventHandler(EventQueue *ev_queue) : _queue(*ev_queue) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public:
|
|||
typedef void EventCallbackFunction(const Event *, void *);
|
||||
|
||||
PUBLISHED:
|
||||
EventHandler(EventQueue *queue);
|
||||
EventHandler(EventQueue *ev_queue);
|
||||
|
||||
void process_events();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ close_framework() {
|
|||
_exit_flag = false;
|
||||
|
||||
_recorder = NULL;
|
||||
|
||||
Thread::prepare_for_exit();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -744,6 +744,8 @@ thread_main() {
|
|||
|
||||
_working_page = NULL;
|
||||
_working_cvar.signal();
|
||||
|
||||
Thread::consider_yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ INLINE ConditionVarDummyImpl::
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void ConditionVarDummyImpl::
|
||||
wait() {
|
||||
Thread::force_yield();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "pandabase.h"
|
||||
#include "selectThreadImpl.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include "pnotify.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"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#ifdef DEBUG_THREADS
|
||||
|
||||
MutexImpl *MutexDebug::_global_lock;
|
||||
MutexTrueImpl *MutexDebug::_global_lock;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: MutexDebug::Constructor
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Doober *> 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; i<N_DINERS; ++i) {
|
||||
phils[i] = new philosopher(i);
|
||||
phils[i]->start(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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 <setjmp.h>
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
jmp_buf buf1, buf2;
|
||||
char * volatile scratch;
|
||||
|
||||
setjmp(buf1);
|
||||
scratch = (char *)alloca(1024);
|
||||
setjmp(buf2);
|
||||
|
||||
size_t word_size = sizeof(buf1[0]);
|
||||
size_t num_words = sizeof(buf1) / word_size;
|
||||
|
||||
cerr << num_words << " words of " << word_size << " bytes\n";
|
||||
for (size_t i = 0; i < num_words; ++i) {
|
||||
cerr << " word " << i << ": " << (void *)buf1[i] << " vs. "
|
||||
<< (void *)buf2[i] << ", delta " << buf1[i] - buf2[i] << "\n";
|
||||
}
|
||||
cerr << "scratch = " << (void *)scratch << "\n";
|
||||
cerr << "scratch end = " << (void *)(scratch + 1024) << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 <Python.h>
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#if !defined(JB_SP) && defined(IS_OSX) && defined(__i386__)
|
||||
// We have determined this value empirically, via test_setjmp.cxx in
|
||||
// this directory.
|
||||
#define JB_SP 9
|
||||
#endif
|
||||
|
||||
class Thread;
|
||||
class ThreadSimpleManager;
|
||||
class MutexSimpleImpl;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ThreadSimpleImpl
|
||||
// Description : This is a trivial threading implementation for
|
||||
// applications that don't desire full OS-managed
|
||||
// threading. It is a user-space implementation of
|
||||
// threads implemented via setjmp/longjmp, and therefore
|
||||
// it cannot take advantage of multiple CPU's (the
|
||||
// application will always run on a single CPU,
|
||||
// regardless of the number of threads you spawn).
|
||||
//
|
||||
// However, since context switching is entirely
|
||||
// cooperative, synchronization primitives like mutexes
|
||||
// and condition variables aren't necessary, and the
|
||||
// Mutex and ConditionVar classes are compiled into
|
||||
// trivial no-op classes, which can reduce overhead
|
||||
// substantially compared to a truly threaded
|
||||
// application.
|
||||
//
|
||||
// Be sure that every thread calls
|
||||
// Thread::consider_yield() occasionally, or it will
|
||||
// starve the rest of the running threads.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA ThreadSimpleImpl {
|
||||
public:
|
||||
ThreadSimpleImpl(Thread *parent_obj);
|
||||
~ThreadSimpleImpl();
|
||||
|
||||
void setup_main_thread();
|
||||
bool start(ThreadPriority priority, bool joinable);
|
||||
void join();
|
||||
void preempt();
|
||||
|
||||
static void prepare_for_exit();
|
||||
|
||||
INLINE static Thread *get_current_thread();
|
||||
INLINE static void bind_thread(Thread *thread);
|
||||
INLINE static bool is_threading_supported();
|
||||
INLINE static void sleep(double seconds);
|
||||
INLINE static void yield();
|
||||
INLINE static void consider_yield();
|
||||
|
||||
void sleep_this(double seconds);
|
||||
void yield_this();
|
||||
INLINE void consider_yield_this();
|
||||
|
||||
INLINE double get_start_time() const;
|
||||
|
||||
private:
|
||||
void setup_context();
|
||||
static void setup_context_2(ThreadSimpleImpl *self);
|
||||
void setup_context_3();
|
||||
|
||||
private:
|
||||
enum Status {
|
||||
S_new,
|
||||
S_running,
|
||||
S_finished,
|
||||
S_killed,
|
||||
};
|
||||
|
||||
Thread *_parent_obj;
|
||||
bool _joinable;
|
||||
Status _status;
|
||||
|
||||
// The (approx) amount of time this thread is allowed to run each
|
||||
// epoch.
|
||||
double _time_per_epoch;
|
||||
|
||||
// This serves both as the time at which the current thread started
|
||||
// to run, and also records the time at which a sleeping thread
|
||||
// should wake up.
|
||||
double _start_time;
|
||||
|
||||
jmp_buf _context;
|
||||
unsigned char *_stack;
|
||||
size_t _stack_size;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
// If we might be working with Python, we have to manage the Python
|
||||
// thread state as we switch contexts.
|
||||
PyThreadState *_python_state;
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
// Threads that are waiting for this thread to finish.
|
||||
typedef pvector<ThreadSimpleImpl *> JoiningThreads;
|
||||
JoiningThreads _joining_threads;
|
||||
|
||||
ThreadSimpleManager *_manager;
|
||||
static ThreadSimpleImpl *volatile _st_this;
|
||||
|
||||
friend class ThreadSimpleManager;
|
||||
};
|
||||
|
||||
// We include this down here to avoid the circularity problem.
|
||||
/* okcircular */
|
||||
#include "threadSimpleManager.h"
|
||||
|
||||
#include "threadSimpleImpl.I"
|
||||
|
||||
#endif // THREAD_SIMPLE_IMPL
|
||||
|
||||
#endif
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 <algorithm>
|
||||
|
||||
class Thread;
|
||||
class ThreadSimpleImpl;
|
||||
class BlockerSimple;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ThreadSimpleManager
|
||||
// Description : This is the global object that selects the
|
||||
// currently-active thread of the various
|
||||
// ThreadSimpleImpl objects running, when the
|
||||
// currently-active thread yields.
|
||||
//
|
||||
// This class only exists when we are using the
|
||||
// ThreadSimple implementation, which is to say, we are
|
||||
// not using "real" threads.
|
||||
//
|
||||
// Generally, you shouldn't be calling these methods
|
||||
// directly. Call the interfaces on Thread instead.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA ThreadSimpleManager {
|
||||
private:
|
||||
ThreadSimpleManager();
|
||||
|
||||
public:
|
||||
void enqueue_ready(ThreadSimpleImpl *thread);
|
||||
void enqueue_sleep(ThreadSimpleImpl *thread, double seconds);
|
||||
void enqueue_block(ThreadSimpleImpl *thread, BlockerSimple *blocker);
|
||||
void unblock_one(BlockerSimple *blocker);
|
||||
void unblock_all(BlockerSimple *blocker);
|
||||
void enqueue_finished(ThreadSimpleImpl *thread);
|
||||
void preempt(ThreadSimpleImpl *thread);
|
||||
void next_context();
|
||||
|
||||
void prepare_for_exit();
|
||||
|
||||
INLINE ThreadSimpleImpl *get_current_thread();
|
||||
void set_current_thread(ThreadSimpleImpl *current_thread);
|
||||
|
||||
INLINE double get_current_time() const;
|
||||
INLINE static ThreadSimpleManager *get_global_ptr();
|
||||
|
||||
private:
|
||||
static void init_pointers();
|
||||
void wake_sleepers(double now);
|
||||
static void system_sleep(double seconds);
|
||||
void report_deadlock();
|
||||
|
||||
// STL function object to sort the priority queue of sleeping threads.
|
||||
class CompareStartTime {
|
||||
public:
|
||||
INLINE bool operator ()(ThreadSimpleImpl *a, ThreadSimpleImpl *b) const;
|
||||
};
|
||||
|
||||
typedef pdeque<ThreadSimpleImpl *> FifoThreads;
|
||||
typedef pvector<ThreadSimpleImpl *> Sleeping;
|
||||
|
||||
void kill_non_joinable(FifoThreads &threads);
|
||||
void kill_non_joinable(Sleeping &threads);
|
||||
|
||||
private:
|
||||
ThreadSimpleImpl *volatile _current_thread;
|
||||
|
||||
// FIFO list of ready threads.
|
||||
FifoThreads _ready;
|
||||
|
||||
typedef pmap<BlockerSimple *, FifoThreads> Blocked;
|
||||
Blocked _blocked;
|
||||
|
||||
// Priority queue (partially-ordered heap) based on wakeup time.
|
||||
Sleeping _sleeping;
|
||||
|
||||
FifoThreads _finished;
|
||||
|
||||
ThreadSimpleImpl *_waiting_for_exit;
|
||||
|
||||
TrueClock *_clock;
|
||||
|
||||
static bool _pointers_initialized;
|
||||
static ThreadSimpleManager *_global_ptr;
|
||||
};
|
||||
|
||||
// We include this down here to avoid the circularity problem.
|
||||
/* okcircular */
|
||||
#include "threadSimpleImpl.h"
|
||||
|
||||
#include "threadSimpleManager.I"
|
||||
|
||||
#endif // THREAD_SIMPLE_IMPL
|
||||
|
||||
#endif
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue