From 16562fc4e5c368517d21e4f6e42c9fd7710ffbea Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 21 Jun 2007 17:57:17 +0000 Subject: [PATCH] linux --- panda/src/pipeline/test_setjmp.cxx | 7 ++++ panda/src/pipeline/threadSimpleImpl.cxx | 28 +++++++++++++- panda/src/pipeline/threadSimpleImpl.h | 30 +++++++++++++-- .../src/pipeline/threadSimpleImpl_no_opt_.cxx | 15 ++++---- panda/src/pipeline/threadSimpleManager.cxx | 38 ++++++++++++++++--- 5 files changed, 100 insertions(+), 18 deletions(-) diff --git a/panda/src/pipeline/test_setjmp.cxx b/panda/src/pipeline/test_setjmp.cxx index f79ebd661a..308d9edde2 100644 --- a/panda/src/pipeline/test_setjmp.cxx +++ b/panda/src/pipeline/test_setjmp.cxx @@ -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; } diff --git a/panda/src/pipeline/threadSimpleImpl.cxx b/panda/src/pipeline/threadSimpleImpl.cxx index 4d038b887d..a252b91ad3 100644 --- a/panda/src/pipeline/threadSimpleImpl.cxx +++ b/panda/src/pipeline/threadSimpleImpl.cxx @@ -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. diff --git a/panda/src/pipeline/threadSimpleImpl.h b/panda/src/pipeline/threadSimpleImpl.h index c8f62b791d..74d61ad846 100644 --- a/panda/src/pipeline/threadSimpleImpl.h +++ b/panda/src/pipeline/threadSimpleImpl.h @@ -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 + +#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 -#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; diff --git a/panda/src/pipeline/threadSimpleImpl_no_opt_.cxx b/panda/src/pipeline/threadSimpleImpl_no_opt_.cxx index 14596bcf44..457c972b25 100644 --- a/panda/src/pipeline/threadSimpleImpl_no_opt_.cxx +++ b/panda/src/pipeline/threadSimpleImpl_no_opt_.cxx @@ -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 diff --git a/panda/src/pipeline/threadSimpleManager.cxx b/panda/src/pipeline/threadSimpleManager.cxx index ae6b91387d..293ef6da1a 100644 --- a/panda/src/pipeline/threadSimpleManager.cxx +++ b/panda/src/pipeline/threadSimpleManager.cxx @@ -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);