open_toontown_panda3d/panda/src/pipeline/contextSwitch.c

228 lines
6.4 KiB
C

/* Filename: contextSwitch.c
* Created by: drose (21Jun07)
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* PANDA 3D SOFTWARE
* Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
*
* All use of this software is subject to the terms of the Panda 3d
* Software license. You should have received a copy of this license
* along with this source code; you will also find a current copy of
* the license at http://etc.cmu.edu/panda3d/docs/license/ .
*
* To contact the maintainers of this program write to
* panda3d-general@lists.sourceforge.net .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "contextSwitch.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef THREAD_SIMPLE_IMPL
#ifdef HAVE_UCONTEXT_H
/* The getcontext() / setcontext() implementation. Easy-peasy. */
static void
begin_context(ContextFunction *thread_func, void *data) {
(*thread_func)(data);
}
void
init_thread_context(struct ThreadContext *context,
unsigned char *stack, size_t stack_size,
ContextFunction *thread_func, void *data) {
getcontext(&context->_ucontext);
context->_ucontext.uc_stack.ss_sp = stack;
context->_ucontext.uc_stack.ss_size = stack_size;
context->_ucontext.uc_stack.ss_flags = 0;
context->_ucontext.uc_link = NULL;
makecontext(&context->_ucontext, (void (*)())&begin_context, 2, thread_func, data);
}
void save_thread_context(struct ThreadContext *context,
ContextFunction *next_context, void *data) {
/* getcontext requires us to use a volatile auto variable to
differentiate between pass 1 (immediate return) and pass 2
(return from setcontext). */
volatile int context_return = 0;
getcontext(&context->_ucontext);
if (context_return) {
/* We have just returned from setcontext. In this case, return
from the function. The stack is still good. */
return;
}
context_return = 1;
/* We are still in the calling thread. In this case, we cannot
return from the function without damaging the stack. Insted,
call next_context() and trust the caller to call
switch_to_thread_context() in there somewhere. */
(*next_context)(data);
/* We shouldn't get here. */
abort();
}
void
switch_to_thread_context(struct ThreadContext *context) {
setcontext(&context->_ucontext);
/* Shouldn't get here. */
abort();
}
#else
/* The setjmp() / longjmp() implementation. A bit hackier. */
/* 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. */
/* Ideally, including 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
#elif defined(WIN32)
/* We have determined this value empirically, via test_setjmp.cxx in
this directory. */
#define JB_SP 4
#endif
#endif /* JB_SP */
static struct ThreadContext *st_context;
static unsigned char *st_stack;
static size_t st_stack_size;
static ContextFunction *st_thread_func;
static void *st_data;
static jmp_buf orig_stack;
static void
setup_context_2(void) {
/* Here we are running on the new stack. Copy the key data onto our
new stack. */
ContextFunction *volatile thread_func = st_thread_func;
void *volatile data = st_data;
if (setjmp(st_context->_jmp_context) == 0) {
/* The _jmp_context is set up and ready to run. Now restore the
original stack and return. We can't simply return from this
function, since it might overwrite some of the stack data on
the way out. */
longjmp(orig_stack, 1);
/* Shouldn't get here. */
abort();
}
/* We come here the first time the thread starts. */
(*thread_func)(data);
/* We shouldn't get here, since we don't expect the thread_func to
return. */
abort();
}
static void
setup_context_1(void) {
/* Save the current stack frame so we can return to it (at the end
of setup_context_2()). */
if (setjmp(orig_stack) == 0) {
/* First, switch to the new stack. Save the current context using
setjmp(). This saves out all of the processor register values,
though it doesn't muck with the stack. */
static jmp_buf temp;
if (setjmp(temp) == 0) {
/* This is the initial return from setjmp. Still the original
stack. */
/* Now we overwrite the stack pointer value in the saved
register context. This doesn't work with all implementations
of setjmp/longjmp. */
(*(void **)&temp[JB_SP]) = (st_stack + st_stack_size);
/* And finally, we place ourselves on the new stack by using
longjmp() to reload the modified context. */
longjmp(temp, 1);
/* Shouldn't get here. */
abort();
}
/* This is the second return from setjmp. Now we're on the new
stack. */
setup_context_2();
/* Shouldn't get here. */
abort();
}
/* By now we are back to the original stack. */
}
void
init_thread_context(struct ThreadContext *context,
unsigned char *stack, size_t stack_size,
ContextFunction *thread_func, void *data) {
/* Copy all of the input parameters to static variables, then begin
the stack-switching process. */
st_context = context;
st_stack = stack;
st_stack_size = stack_size;
st_thread_func = thread_func;
st_data = data;
setup_context_1();
}
void save_thread_context(struct ThreadContext *context,
ContextFunction *next_context, void *data) {
if (setjmp(context->_jmp_context) != 0) {
/* We have just returned from longjmp. In this case, return from
the function. The stack is still good. */
return;
}
/* We are still in the calling thread. In this case, we cannot
return from the function without damaging the stack. Insted,
call next_context() and trust the caller to call
switch_to_thread_context() in there somewhere. */
(*next_context)(data);
/* We shouldn't get here. */
abort();
}
void
switch_to_thread_context(struct ThreadContext *context) {
longjmp(context->_jmp_context, 1)
/* Shouldn't get here. */
abort();
}
#endif /* HAVE_UCONTEXT_H */
#endif /* THREAD_SIMPLE_IMPL */