This commit is contained in:
David Rose 2007-06-21 17:57:17 +00:00
parent 050541c214
commit 16562fc4e5
5 changed files with 100 additions and 18 deletions

View File

@ -22,6 +22,12 @@
int int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
// If we have ucontext.h, we don't need to use setjmp, so don't
// bother trying to compile this program (it may not compile
// anyway).
#ifndef HAVE_UCONTEXT_H
jmp_buf buf1, buf2; jmp_buf buf1, buf2;
char * volatile scratch; char * volatile scratch;
@ -39,6 +45,7 @@ main(int argc, char *argv[]) {
} }
cerr << "scratch = " << (void *)scratch << "\n"; cerr << "scratch = " << (void *)scratch << "\n";
cerr << "scratch end = " << (void *)(scratch + 1024) << "\n"; cerr << "scratch end = " << (void *)(scratch + 1024) << "\n";
#endif // HAVE_UCONTEXT_H
return 0; return 0;
} }

View File

@ -196,11 +196,34 @@ yield_this() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: ThreadSimpleImpl::setup_context // Function: ThreadSimpleImpl::setup_context
// Access: Private // Access: Private
// Description: Fills the _context with an appropriate context buffer // Description: Fills the _jmp_context with an appropriate context buffer
// and an appropriate stack reserved for the thread. // and an appropriate stack reserved for the thread.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void ThreadSimpleImpl:: void ThreadSimpleImpl::
setup_context() { setup_context() {
#ifdef HAVE_UCONTEXT_H
// Set up a unique thread context using makecontext().
getcontext(&_ucontext);
_ucontext.uc_stack.ss_sp = _stack;
_ucontext.uc_stack.ss_size = _stack_size;
_ucontext.uc_stack.ss_flags = 0;
_ucontext.uc_link = NULL;
makecontext(&_ucontext, (void (*)())setup_context_2, 1, this);
#else // HAVE_UCONTEXT_H
// The setjmp hack for setting up a unique thread context is a bit
// more complicated. The approach is: hack our way onto the new
// stack pointer right now, then call setjmp() to record that stack
// pointer in the _jmp_context. Then restore back to the original
// stack pointer.
// This requires jumping through a couple of different functions.
// One of these functions, setup_context_2(), is defined in a
// different file, so we can easily disable compiler optimizations
// on that one function.
jmp_buf orig_stack; jmp_buf orig_stack;
if (setjmp(orig_stack) == 0) { if (setjmp(orig_stack) == 0) {
// First, switch to the new stack. This requires temporarily saving // First, switch to the new stack. This requires temporarily saving
@ -234,11 +257,12 @@ setup_context() {
} }
// By now we are back to the original stack. // By now we are back to the original stack.
#endif // HAVE_UCONTEXT_H
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: ThreadSimpleImpl::setup_context_3 // Function: ThreadSimpleImpl::setup_context_3
// Access: Private // Access: Private, Static
// Description: More continuation of setup_context(). Again, making // Description: More continuation of setup_context(). Again, making
// this a separate function helps defeat the compiler // this a separate function helps defeat the compiler
// optimizer. // optimizer.

View File

@ -35,9 +35,24 @@
#endif // HAVE_PYTHON #endif // HAVE_PYTHON
#ifdef HAVE_UCONTEXT_H
// We'd prefer to use getcontext() / setcontext() to portably change
// execution contexts within C code. That's what these library
// functions are designed for.
#include <ucontext.h>
#else
// Unfortunately, setcontext() is not defined everywhere (even though
// it claims to be adopted by Posix). So we have to fall back to
// setjmp() / longjmp() in its absence. This is a hackier solution.
#include <setjmp.h> #include <setjmp.h>
#if !defined(JB_SP) && defined(IS_OSX) && defined(__i386__) // Ideally, setjmp.h would have defined JB_SP, which will tell us
// where in the context structure we can muck with the stack pointer.
// If it didn't define this symbol, we have to guess it.
#ifndef JB_SP
#if defined(IS_OSX) && defined(__i386__)
// We have determined this value empirically, via test_setjmp.cxx in // We have determined this value empirically, via test_setjmp.cxx in
// this directory. // this directory.
#define JB_SP 9 #define JB_SP 9
@ -49,6 +64,10 @@
#endif #endif
#endif // JB_SP
#endif // HAVE_UCONTEXT_H
class Thread; class Thread;
class ThreadSimpleManager; class ThreadSimpleManager;
class MutexSimpleImpl; class MutexSimpleImpl;
@ -102,7 +121,7 @@ public:
private: private:
void setup_context(); void setup_context();
static void setup_context_2(ThreadSimpleImpl *self); static void *setup_context_2(ThreadSimpleImpl *self);
void setup_context_3(); void setup_context_3();
private: private:
@ -126,7 +145,12 @@ private:
// should wake up. // should wake up.
double _start_time; double _start_time;
jmp_buf _context; #ifdef HAVE_UCONTEXT_H
ucontext_t _ucontext;
#else
jmp_buf _jmp_context;
#endif // HAVE_UCONTEXT_H
unsigned char *_stack; unsigned char *_stack;
size_t _stack_size; size_t _stack_size;

View File

@ -31,24 +31,23 @@
// disable optimizations globally on the command line, // disable optimizations globally on the command line,
// not via pragmas). // not via pragmas).
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void ThreadSimpleImpl:: void *ThreadSimpleImpl::
setup_context_2(ThreadSimpleImpl *self) { setup_context_2(ThreadSimpleImpl *self) {
ThreadSimpleImpl *volatile v_self = self; ThreadSimpleImpl *volatile v_self = self;
if (setjmp(self->_context) == 0) {
// The _context is now set up and ready to run. Now we can #ifndef HAVE_UCONTEXT_H
if (setjmp(self->_jmp_context) == 0) {
// The _jmp_context is now set up and ready to run. Now we can
// return. // return.
return; return;
} }
#endif // HAVE_UCONTEXT_H
// Here we are executing within the thread. // Here we are executing within the thread.
v_self->setup_context_3(); 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(); abort();
return v_self;
} }
#endif // THREAD_SIMPLE_IMPL #endif // THREAD_SIMPLE_IMPL

View File

@ -38,9 +38,9 @@ ThreadSimpleManager *ThreadSimpleManager::_global_ptr;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
ThreadSimpleManager:: ThreadSimpleManager::
ThreadSimpleManager() { ThreadSimpleManager() {
_current_thread = NULL;
_clock = TrueClock::get_global_ptr(); _clock = TrueClock::get_global_ptr();
_waiting_for_exit = NULL; _waiting_for_exit = NULL;
nassertv(_current_thread == NULL);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -49,7 +49,7 @@ ThreadSimpleManager() {
// Description: Adds the indicated thread to the ready queue. The // Description: Adds the indicated thread to the ready queue. The
// thread will be executed when its turn comes. If the // thread will be executed when its turn comes. If the
// thread is not the currently executing thread, its // thread is not the currently executing thread, its
// _context should be filled appropriately. // _jmp_context should be filled appropriately.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void ThreadSimpleManager:: void ThreadSimpleManager::
enqueue_ready(ThreadSimpleImpl *thread) { enqueue_ready(ThreadSimpleImpl *thread) {
@ -173,7 +173,7 @@ preempt(ThreadSimpleImpl *thread) {
// re-enqueued itself with a call to enqueue(), if it // re-enqueued itself with a call to enqueue(), if it
// intends to run again. // intends to run again.
// //
// This will fill in the current thread's _context // This will fill in the current thread's _jmp_context
// member appropriately, and then change the global // member appropriately, and then change the global
// current_thread pointer. // current_thread pointer.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -187,7 +187,29 @@ next_context() {
PyThreadState_Swap(_current_thread->_python_state); PyThreadState_Swap(_current_thread->_python_state);
#endif // HAVE_PYTHON #endif // HAVE_PYTHON
if (setjmp(_current_thread->_context) != 0) { #ifdef HAVE_UCONTEXT_H
// The setcontext() implementation.
volatile bool context_return = false;
getcontext(&_current_thread->_ucontext);
if (context_return) {
// We have returned from a setcontext, and are now resuming the
// current thread.
#ifdef HAVE_PYTHON
PyThreadState_Swap(_current_thread->_python_state);
#endif // HAVE_PYTHON
return;
}
// Set this flag so that we can differentiate between getcontext()
// returning the first time and the second time.
context_return = true;
#else
// The longjmp() implementation.
if (setjmp(_current_thread->_jmp_context) != 0) {
// We have returned from a longjmp, and are now resuming the // We have returned from a longjmp, and are now resuming the
// current thread. // current thread.
#ifdef HAVE_PYTHON #ifdef HAVE_PYTHON
@ -196,6 +218,7 @@ next_context() {
return; return;
} }
#endif // HAVE_UCONTEXT_H
while (!_finished.empty() && _finished.front() != _current_thread) { while (!_finished.empty() && _finished.front() != _current_thread) {
ThreadSimpleImpl *finished_thread = _finished.front(); ThreadSimpleImpl *finished_thread = _finished.front();
@ -261,7 +284,12 @@ next_context() {
thread_cat.spam() thread_cat.spam()
<< "Switching to " << *_current_thread->_parent_obj << "\n"; << "Switching to " << *_current_thread->_parent_obj << "\n";
} }
longjmp(_current_thread->_context, 1);
#ifdef HAVE_UCONTEXT_H
setcontext(&_current_thread->_ucontext);
#else
longjmp(_current_thread->_jmp_context, 1);
#endif
// Shouldn't get here. // Shouldn't get here.
nassertv(false); nassertv(false);