first pass at SIMPLE_THREADS

This commit is contained in:
David Rose 2007-06-21 15:33:27 +00:00
parent cd833ba93b
commit 8d2bdc39fd
69 changed files with 2482 additions and 143 deletions

View File

@ -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

View File

@ -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]

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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";

View File

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

View File

@ -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

View File

@ -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; };

View File

@ -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()

View File

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

View File

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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

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

View File

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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -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"

View File

@ -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();
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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 {

View File

@ -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.

View File

@ -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();
}

View File

@ -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);

View File

@ -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() {
}

View File

@ -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"

View File

@ -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"

View File

@ -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() {
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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

View File

@ -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() {
}

View File

@ -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);

View File

@ -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);

View File

@ -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()