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
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;
char * volatile scratch;
@ -39,6 +45,7 @@ main(int argc, char *argv[]) {
}
cerr << "scratch = " << (void *)scratch << "\n";
cerr << "scratch end = " << (void *)(scratch + 1024) << "\n";
#endif // HAVE_UCONTEXT_H
return 0;
}

View File

@ -196,11 +196,34 @@ yield_this() {
////////////////////////////////////////////////////////////////////
// Function: ThreadSimpleImpl::setup_context
// 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.
////////////////////////////////////////////////////////////////////
void ThreadSimpleImpl::
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;
if (setjmp(orig_stack) == 0) {
// 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.
#endif // HAVE_UCONTEXT_H
}
////////////////////////////////////////////////////////////////////
// Function: ThreadSimpleImpl::setup_context_3
// Access: Private
// Access: Private, Static
// Description: More continuation of setup_context(). Again, making
// this a separate function helps defeat the compiler
// optimizer.

View File

@ -35,9 +35,24 @@
#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>
#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
// this directory.
#define JB_SP 9
@ -49,6 +64,10 @@
#endif
#endif // JB_SP
#endif // HAVE_UCONTEXT_H
class Thread;
class ThreadSimpleManager;
class MutexSimpleImpl;
@ -102,7 +121,7 @@ public:
private:
void setup_context();
static void setup_context_2(ThreadSimpleImpl *self);
static void *setup_context_2(ThreadSimpleImpl *self);
void setup_context_3();
private:
@ -126,7 +145,12 @@ private:
// should wake up.
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;
size_t _stack_size;

View File

@ -31,24 +31,23 @@
// disable optimizations globally on the command line,
// not via pragmas).
////////////////////////////////////////////////////////////////////
void ThreadSimpleImpl::
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
#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;
}
#endif // HAVE_UCONTEXT_H
// 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();
return v_self;
}
#endif // THREAD_SIMPLE_IMPL

View File

@ -38,9 +38,9 @@ ThreadSimpleManager *ThreadSimpleManager::_global_ptr;
////////////////////////////////////////////////////////////////////
ThreadSimpleManager::
ThreadSimpleManager() {
_current_thread = NULL;
_clock = TrueClock::get_global_ptr();
_waiting_for_exit = NULL;
nassertv(_current_thread == NULL);
}
////////////////////////////////////////////////////////////////////
@ -49,7 +49,7 @@ ThreadSimpleManager() {
// 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.
// _jmp_context should be filled appropriately.
////////////////////////////////////////////////////////////////////
void ThreadSimpleManager::
enqueue_ready(ThreadSimpleImpl *thread) {
@ -173,7 +173,7 @@ preempt(ThreadSimpleImpl *thread) {
// re-enqueued itself with a call to enqueue(), if it
// 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
// current_thread pointer.
////////////////////////////////////////////////////////////////////
@ -187,7 +187,29 @@ next_context() {
PyThreadState_Swap(_current_thread->_python_state);
#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
// current thread.
#ifdef HAVE_PYTHON
@ -196,6 +218,7 @@ next_context() {
return;
}
#endif // HAVE_UCONTEXT_H
while (!_finished.empty() && _finished.front() != _current_thread) {
ThreadSimpleImpl *finished_thread = _finished.front();
@ -261,7 +284,12 @@ next_context() {
thread_cat.spam()
<< "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.
nassertv(false);