Browse Source

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

David Rose 16 years ago
parent
commit
7bc69b76ed

+ 96 - 2
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 <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 */

+ 8 - 55
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 <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 

+ 4 - 1
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;

+ 1 - 1
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;
 

+ 0 - 12
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

+ 14 - 2
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);

+ 1 - 1
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;