osx ucontext issues popping up again. This should fix them for good.

This commit is contained in:
David Rose 2009-10-11 18:45:33 +00:00
parent 90f0c9d671
commit 7bc69b76ed
7 changed files with 124 additions and 74 deletions

View File

@ -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 <ucontext.h>
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 <setjmp.h>
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 */

View File

@ -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 <ucontext.h>
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 <setjmp.h>
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

View File

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

View File

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

View File

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

View File

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

View File

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