From 7bc69b76ed75cdc73cba13f8d5a5ef2202842612 Mon Sep 17 00:00:00 2001 From: David Rose Date: Sun, 11 Oct 2009 18:45:33 +0000 Subject: [PATCH] osx ucontext issues popping up again. This should fix them for good. --- panda/src/pipeline/contextSwitch.c | 98 +++++++++++++++++++++- panda/src/pipeline/contextSwitch.h | 63 ++------------ panda/src/pipeline/threadSimpleImpl.cxx | 5 +- panda/src/pipeline/threadSimpleImpl.h | 2 +- panda/src/pipeline/threadSimpleManager.I | 12 --- panda/src/pipeline/threadSimpleManager.cxx | 16 +++- panda/src/pipeline/threadSimpleManager.h | 2 +- 7 files changed, 124 insertions(+), 74 deletions(-) diff --git a/panda/src/pipeline/contextSwitch.c b/panda/src/pipeline/contextSwitch.c index 5e1f01e13e..a070959266 100644 --- a/panda/src/pipeline/contextSwitch.c +++ b/panda/src/pipeline/contextSwitch.c @@ -21,7 +21,27 @@ #if defined(PHAVE_UCONTEXT_H) -/* The getcontext() / setcontext() implementation. Easy-peasy. */ +#else /* PHAVE_UCONTEXT_H */ + +#endif /* PHAVE_UCONTEXT_H */ + +#if defined(PHAVE_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 + +struct ThreadContext { + ucontext_t _ucontext; +#ifdef __APPLE__ + // Due to a bug in OSX 10.5, the system ucontext_t declaration + // doesn't reserve enough space, and we need to reserve some + // additional space to make room. +#define EXTRA_PADDING_SIZE 4096 + char _extra_padding[EXTRA_PADDING_SIZE]; +#endif +}; static void begin_context(ContextFunction *thread_func, void *data) { @@ -89,7 +109,42 @@ switch_to_thread_context(struct ThreadContext *context) { #else -/* The setjmp() / longjmp() implementation. A bit hackier. */ +/* 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. */ + +#if defined(_M_IX86) || defined(__i386__) +/* Maybe we can implement our own setjmp/longjmp in assembly code. + This will be safer than the system version, since who knows what + that one's really doing? */ + +typedef int cs_jmp_buf[33]; + +#define CS_JB_SP 4 + +#else + +/* Fall back to the system implmentation of setjmp/longjmp. */ +#include + +typedef jmp_buf cs_jmp_buf; +#define cs_setjmp setjmp +#define cs_longjmp(buf) longjmp(buf, 1) + +#ifdef JB_SP +#define CS_JB_SP JB_SP + +#elif defined(__ppc__) + /* This was determined experimentally through test_setjmp. */ +#define CS_JB_SP 0 + +#endif + +#endif /* __i386__ */ + +struct ThreadContext { + cs_jmp_buf _jmp_context; +}; /* The approach is: hack our way onto the new stack pointer right now, then call setjmp() to record that stack pointer in the @@ -333,4 +388,43 @@ switch_to_thread_context(struct ThreadContext *context) { } #endif /* PHAVE_UCONTEXT_H */ + +struct ThreadContext * +alloc_thread_context() { + struct ThreadContext *context = + (struct ThreadContext *)malloc(sizeof(struct ThreadContext)); + +#if defined(__APPLE__) && defined(_DEBUG) + { + int p; + // Pre-fill the extra_padding with bytes that we can recognize + // later. + for (p = 0; p < EXTRA_PADDING_SIZE; ++p) { + context->_extra_padding[p] = (p & 0xff); + } + } +#endif // __APPLE__ + + return context; +} + +void +free_thread_context(struct ThreadContext *context) { +#if defined(__APPLE__) && defined(_DEBUG) + { + // Because of the OSX 10.5 bug, we anticipate that the extra_padding + // may have been filled in with junk. Confirm this. + int p = EXTRA_PADDING_SIZE; + while (p > 0) { + --p; + if (context->_extra_padding[p] != (char)(p & 0xff)) { + fprintf(stderr, "Context was mangled at byte %d: %d!\n", p, context->_extra_padding[p]); + break; + } + } + } +#endif // __APPLE__ + free(context); +} + #endif /* THREAD_SIMPLE_IMPL */ diff --git a/panda/src/pipeline/contextSwitch.h b/panda/src/pipeline/contextSwitch.h index e32942f143..bacfaf4daf 100644 --- a/panda/src/pipeline/contextSwitch.h +++ b/panda/src/pipeline/contextSwitch.h @@ -30,61 +30,7 @@ #ifdef THREAD_SIMPLE_IMPL -#if defined(PHAVE_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 - -struct ThreadContext { - ucontext_t _ucontext; -#if defined(__APPLE__) && defined(_STRUCT_MCONTEXT) - // Due to a bug in OSX 10.5, the system ucontext_t declaration - // doesn't reserve enough space, and we need to reserve some - // additional space to make room. - _STRUCT_MCONTEXT _extra_padding; -#endif -}; - -#else /* PHAVE_UCONTEXT_H */ -/* 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. */ - -#if defined(_M_IX86) || defined(__i386__) -/* Maybe we can implement our own setjmp/longjmp in assembly code. - This will be safer than the system version, since who knows what - that one's really doing? */ - -typedef int cs_jmp_buf[33]; - -#define CS_JB_SP 4 - -#else - -/* Fall back to the system implmentation of setjmp/longjmp. */ -#include - -typedef jmp_buf cs_jmp_buf; -#define cs_setjmp setjmp -#define cs_longjmp(buf) longjmp(buf, 1) - -#ifdef JB_SP -#define CS_JB_SP JB_SP - -#elif defined(__ppc__) - /* This was determined experimentally through test_setjmp. */ -#define CS_JB_SP 0 - -#endif - -#endif /* __i386__ */ - -struct ThreadContext { - cs_jmp_buf _jmp_context; -}; - -#endif /* PHAVE_UCONTEXT_H */ +struct ThreadContext; #ifdef __cplusplus extern "C" { @@ -112,6 +58,13 @@ void save_thread_context(struct ThreadContext *context, stack (or begin executing thread_func()). */ void switch_to_thread_context(struct ThreadContext *context); +/* Use this pair of functions to transparently allocate and destroy an + opaque ThreadContext object of the appropriate size. These + functions only allocate memory; they do not initialize the values + of the context (see init_thread_context(), above, for that). */ +struct ThreadContext *alloc_thread_context(); +void free_thread_context(struct ThreadContext *context); + #ifdef __cplusplus } #endif diff --git a/panda/src/pipeline/threadSimpleImpl.cxx b/panda/src/pipeline/threadSimpleImpl.cxx index 4dc51ac157..2000f69f5d 100644 --- a/panda/src/pipeline/threadSimpleImpl.cxx +++ b/panda/src/pipeline/threadSimpleImpl.cxx @@ -45,6 +45,7 @@ ThreadSimpleImpl(Thread *parent_obj) : _stop_time = 0.0; _wake_time = 0.0; + _context = alloc_thread_context(); _stack = NULL; _stack_size = 0; @@ -65,6 +66,8 @@ ThreadSimpleImpl:: } nassertv(_status != TS_running); + free_thread_context(_context); + if (_stack != (void *)NULL) { memory_hook->mmap_free(_stack, _stack_size); } @@ -136,7 +139,7 @@ start(ThreadPriority priority, bool joinable) { PyThreadState_Swap(_python_state); #endif // HAVE_PYTHON - init_thread_context(&_context, _stack, _stack_size, st_begin_thread, this); + init_thread_context(_context, _stack, _stack_size, st_begin_thread, this); _manager->enqueue_ready(this, false); return true; diff --git a/panda/src/pipeline/threadSimpleImpl.h b/panda/src/pipeline/threadSimpleImpl.h index 9b1daad380..051a086e31 100644 --- a/panda/src/pipeline/threadSimpleImpl.h +++ b/panda/src/pipeline/threadSimpleImpl.h @@ -125,7 +125,7 @@ private: // This records the time at which a sleeping thread should wake up. double _wake_time; - ThreadContext _context; + ThreadContext *_context; unsigned char *_stack; size_t _stack_size; diff --git a/panda/src/pipeline/threadSimpleManager.I b/panda/src/pipeline/threadSimpleManager.I index 4ab849632d..0818ca071f 100644 --- a/panda/src/pipeline/threadSimpleManager.I +++ b/panda/src/pipeline/threadSimpleManager.I @@ -42,18 +42,6 @@ is_same_system_thread() const { return true; } -//////////////////////////////////////////////////////////////////// -// 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 diff --git a/panda/src/pipeline/threadSimpleManager.cxx b/panda/src/pipeline/threadSimpleManager.cxx index 305e8f1b43..e00e92b346 100644 --- a/panda/src/pipeline/threadSimpleManager.cxx +++ b/panda/src/pipeline/threadSimpleManager.cxx @@ -276,7 +276,7 @@ next_context() { } #endif // DO_PSTATS - save_thread_context(&_current_thread->_context, st_choose_next_context, this); + save_thread_context(_current_thread->_context, st_choose_next_context, this); // Pass 2: we have returned into the context, and are now resuming // the current thread. @@ -490,6 +490,18 @@ system_yield() { #endif // WIN32 } +//////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////// +double ThreadSimpleManager:: +get_current_time() const { + return _clock->get_short_raw_time(); +} + //////////////////////////////////////////////////////////////////// // Function: ThreadSimpleManager::init_pointers // Access: Private, Static @@ -672,7 +684,7 @@ choose_next_context() { << " blocked, " << _sleeping.size() << " sleeping)\n"; } - switch_to_thread_context(&_current_thread->_context); + switch_to_thread_context(_current_thread->_context); // Shouldn't get here. nassertv(false); diff --git a/panda/src/pipeline/threadSimpleManager.h b/panda/src/pipeline/threadSimpleManager.h index 2159608775..f74594d4cf 100644 --- a/panda/src/pipeline/threadSimpleManager.h +++ b/panda/src/pipeline/threadSimpleManager.h @@ -76,7 +76,7 @@ public: static void system_sleep(double seconds); static void system_yield(); - INLINE double get_current_time() const; + double get_current_time() const; INLINE static ThreadSimpleManager *get_global_ptr(); void write_status(ostream &out) const;