From a027efa5bfa7911b5c4b522b6a0698749a6f2e4a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 5 May 1997 20:56:21 +0000 Subject: Massive changes for separate thread state management. All per-thread globals are moved into a struct which is manipulated separately. --- Include/Python.h | 2 + Include/frameobject.h | 4 +- Include/pystate.h | 134 ++++++++++++++++ Include/pythread.h | 9 ++ Include/thread.h | 9 ++ Modules/threadmodule.c | 91 +++++++---- Objects/frameobject.c | 15 +- Python/Makefile.in | 3 +- Python/ceval.c | 414 +++++++++++++++++++++++++++++++------------------ Python/errors.c | 76 +++++---- Python/pystate.c | 163 +++++++++++++++++++ Python/pythonrun.c | 50 +++++- Python/sysmodule.c | 39 +++-- Python/thread_sgi.h | 80 ++++++++++ Python/traceback.c | 36 +---- 15 files changed, 864 insertions(+), 261 deletions(-) create mode 100644 Include/pystate.h create mode 100644 Python/pystate.c diff --git a/Include/Python.h b/Include/Python.h index ffc7e2c..d13003d 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -95,6 +95,8 @@ PERFORMANCE OF THIS SOFTWARE. #include "import.h" #include "bltinmodule.h" +#include "pystate.h" + #include "abstract.h" #define PyArg_GetInt(v, a) PyArg_Parse((v), "i", (a)) diff --git a/Include/frameobject.h b/Include/frameobject.h index 22cc23c..157cba3 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -52,6 +52,8 @@ typedef struct _frame { PyObject *f_locals; /* local symbol table (PyDictObject) */ PyObject **f_valuestack; /* points after the last local */ PyObject *f_trace; /* Trace function */ + PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; + PyThreadState *f_tstate; int f_lasti; /* Last instruction if called */ int f_lineno; /* Current line number */ int f_restricted; /* Flag set if restricted operations @@ -71,7 +73,7 @@ extern DL_IMPORT(PyTypeObject) PyFrame_Type; #define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type) PyFrameObject * PyFrame_New - Py_PROTO((PyFrameObject *, PyCodeObject *, + Py_PROTO((PyThreadState *, PyCodeObject *, PyObject *, PyObject *)); diff --git a/Include/pystate.h b/Include/pystate.h new file mode 100644 index 0000000..514c46d --- /dev/null +++ b/Include/pystate.h @@ -0,0 +1,134 @@ +#ifndef Py_PYSTATE_H +#define Py_PYSTATE_H +#ifdef __cplusplus +extern "C" { +#endif + +/*********************************************************** +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Thread and interpreter state structures and their interfaces */ + + +/* State shared between threads */ + +#define NEXITFUNCS 32 + +typedef struct _is { + + PyObject *import_modules; + PyObject *sysdict; + + int nthreads; + + void (*exitfuncs[NEXITFUNCS])(); + int nexitfuncs; + +} PyInterpreterState; + + +/* State unique per thread */ + +struct _frame; /* Avoid including frameobject.h */ + +typedef struct _ts { + + PyInterpreterState *interpreter_state; + + struct _frame *frame; + int recursion_depth; + int ticker; + int tracing; + + PyObject *sys_profilefunc; + PyObject *sys_tracefunc; + int sys_checkinterval; + + PyObject *curexc_type; + PyObject *curexc_value; + PyObject *curexc_traceback; + + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_traceback; + + /* XXX Other state that should be here: + - signal handlers + - low-level "pending calls" + Problem with both is that they may be referenced from + interrupt handlers where there is no clear concept of a + "current thread"??? + */ + +} PyThreadState; + + +PyInterpreterState *PyInterpreterState_New(void); +void PyInterpreterState_Delete(PyInterpreterState *); + +PyThreadState *PyThreadState_New(PyInterpreterState *); +void PyThreadState_Delete(PyThreadState *); + +PyThreadState *PyThreadState_Get(void); +PyThreadState *PyThreadState_Swap(PyThreadState *); + +/* Some background. + + There are lots of issues here. + + First, we can build Python without threads, with threads, or (when + Greg Stein's mods are out of beta, on some platforms) with free + threading. + + Next, assuming some form of threading is used, there can be several + kinds of threads. Python code can create threads with the thread + module. C code can create threads with the interface defined in + python's "thread.h". Or C code can create threads directly with + the OS threads interface (e.g. Solaris threads, SGI threads or + pthreads, whatever is being used, as long as it's the same that + Python is configured for). + + Next, let's discuss sharing of interpreter state between threads. + The exception state (sys.exc_* currently) should never be shared + between threads, because it is stack frame specific. The contents + of the sys module, in particular sys.modules and sys.path, are + generally shared between threads. But occasionally it is useful to + have separate module collections, e.g. when threads originate in C + code and are used to execute unrelated Python scripts. + (Traditionally, one would use separate processes for this, but + there are lots of reasons why threads are attractive.) + +*/ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYSTATE_H */ diff --git a/Include/pythread.h b/Include/pythread.h index f42e52f..09415c9 100644 --- a/Include/pythread.h +++ b/Include/pythread.h @@ -35,6 +35,10 @@ extern "C" { #define up_sema PyThread_up_sema #define exit_prog PyThread_exit_prog #define _exit_prog PyThread__exit_prog +#define create_key PyThread_create_key +#define delete_key PyThread_delete_key +#define get_key_value PyThread_get_key_value +#define set_key_value PyThread_set_key_value void init_thread Py_PROTO((void)); @@ -62,6 +66,11 @@ void exit_prog Py_PROTO((int)); void _exit_prog Py_PROTO((int)); #endif +int create_key Py_PROTO((void)); +void delete_key Py_PROTO((int)); +int set_key_value Py_PROTO((int, void *)); +void * get_key_value Py_PROTO((int)); + #ifdef __cplusplus } #endif diff --git a/Include/thread.h b/Include/thread.h index f42e52f..09415c9 100644 --- a/Include/thread.h +++ b/Include/thread.h @@ -35,6 +35,10 @@ extern "C" { #define up_sema PyThread_up_sema #define exit_prog PyThread_exit_prog #define _exit_prog PyThread__exit_prog +#define create_key PyThread_create_key +#define delete_key PyThread_delete_key +#define get_key_value PyThread_get_key_value +#define set_key_value PyThread_set_key_value void init_thread Py_PROTO((void)); @@ -62,6 +66,11 @@ void exit_prog Py_PROTO((int)); void _exit_prog Py_PROTO((int)); #endif +int create_key Py_PROTO((void)); +void delete_key Py_PROTO((int)); +int set_key_value Py_PROTO((int, void *)); +void * get_key_value Py_PROTO((int)); + #ifdef __cplusplus } #endif diff --git a/Modules/threadmodule.c b/Modules/threadmodule.c index 461953e..99ca4af 100644 --- a/Modules/threadmodule.c +++ b/Modules/threadmodule.c @@ -35,14 +35,13 @@ PERFORMANCE OF THIS SOFTWARE. #include "Python.h" #ifndef WITH_THREAD -Error! The rest of Python is not compiled with thread support. -Rerun configure, adding a --with-thread option. +#error "Error! The rest of Python is not compiled with thread support." +#error "Rerun configure, adding a --with-thread option." +#error "Then run `make clean' followed by `make'." #endif #include "thread.h" -extern int _PyThread_Started; - static PyObject *ThreadError; @@ -192,52 +191,90 @@ static PyTypeObject Locktype = { /* Module functions */ +struct bootstate { + PyInterpreterState *interp; + PyObject *func; + PyObject *args; + PyObject *keyw; +}; + static void -t_bootstrap(args_raw) - void *args_raw; +t_bootstrap(boot_raw) + void *boot_raw; { - PyObject *args = (PyObject *) args_raw; - PyObject *func, *arg, *res; - - _PyThread_Started++; + struct bootstate *boot = (struct bootstate *) boot_raw; + PyThreadState *alttstate, *tstate; + PyObject *res; + tstate = PyThreadState_New(boot->interp); PyEval_RestoreThread((void *)NULL); - func = PyTuple_GetItem(args, 0); - arg = PyTuple_GetItem(args, 1); - res = PyEval_CallObject(func, arg); - Py_DECREF(args); /* Matches the INCREF(args) in thread_start_new_thread */ + alttstate = PyThreadState_Swap(tstate); + res = PyEval_CallObjectWithKeywords( + boot->func, boot->args, boot->keyw); + Py_DECREF(boot->func); + Py_DECREF(boot->args); + Py_XDECREF(boot->keyw); + PyMem_DEL(boot_raw); if (res == NULL) { if (PyErr_Occurred() == PyExc_SystemExit) PyErr_Clear(); else { fprintf(stderr, "Unhandled exception in thread:\n"); - PyErr_Print(); /* From pythonmain.c */ + PyErr_Print(); } } else Py_DECREF(res); - (void) PyEval_SaveThread(); /* Should always be NULL */ + (void) PyThreadState_Swap(alttstate); + (void) PyEval_SaveThread(); + PyThreadState_Delete(tstate); exit_thread(); } static PyObject * -thread_start_new_thread(self, args) +thread_start_new_thread(self, fargs) PyObject *self; /* Not used */ - PyObject *args; + PyObject *fargs; { - PyObject *func, *arg; + PyObject *func, *args = NULL, *keyw = NULL; + struct bootstate *boot; - if (!PyArg_Parse(args, "(OO)", &func, &arg)) + if (!PyArg_ParseTuple(fargs, "OO|O", &func, &args, &keyw)) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, + "first arg must be callable"); return NULL; + } + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, + "optional 2nd arg must be a tuple"); + return NULL; + } + if (keyw != NULL && !PyDict_Check(keyw)) { + PyErr_SetString(PyExc_TypeError, + "optional 3rd arg must be a dictionary"); + return NULL; + } + boot = PyMem_NEW(struct bootstate, 1); + if (boot == NULL) + return PyErr_NoMemory(); + boot->interp = PyThreadState_Get()->interpreter_state; + boot->func = func; + boot->args = args; + boot->keyw = keyw; + Py_INCREF(func); Py_INCREF(args); - /* Initialize the interpreter's stack save/restore mechanism */ - PyEval_InitThreads(); - if (!start_new_thread(t_bootstrap, (void*) args)) { - Py_DECREF(args); + Py_XINCREF(keyw); + PyEval_InitThreads(); /* Start the interpreter's thread-awareness */ + if (!start_new_thread(t_bootstrap, (void*) boot)) { PyErr_SetString(ThreadError, "can't start new thread\n"); + Py_DECREF(func); + Py_DECREF(args); + Py_XDECREF(keyw); + PyMem_DEL(boot); return NULL; } - /* Otherwise the DECREF(args) is done by t_bootstrap */ Py_INCREF(Py_None); return Py_None; } @@ -294,8 +331,8 @@ thread_get_ident(self, args) } static PyMethodDef thread_methods[] = { - {"start_new_thread", (PyCFunction)thread_start_new_thread}, - {"start_new", (PyCFunction)thread_start_new_thread}, + {"start_new_thread", (PyCFunction)thread_start_new_thread, 1}, + {"start_new", (PyCFunction)thread_start_new_thread, 1}, {"allocate_lock", (PyCFunction)thread_allocate_lock}, {"allocate", (PyCFunction)thread_allocate_lock}, {"exit_thread", (PyCFunction)thread_exit_thread}, diff --git a/Objects/frameobject.c b/Objects/frameobject.c index b5af7b6..4ee1fe5 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -50,6 +50,9 @@ static struct memberlist frame_memberlist[] = { {"f_lineno", T_INT, OFF(f_lineno), RO}, {"f_restricted",T_INT, OFF(f_restricted),RO}, {"f_trace", T_OBJECT, OFF(f_trace)}, + {"f_exc_type", T_OBJECT, OFF(f_exc_type)}, + {"f_exc_value", T_OBJECT, OFF(f_exc_value)}, + {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, {NULL} /* Sentinel */ }; @@ -112,6 +115,9 @@ frame_dealloc(f) Py_XDECREF(f->f_globals); Py_XDECREF(f->f_locals); Py_XDECREF(f->f_trace); + Py_XDECREF(f->f_exc_type); + Py_XDECREF(f->f_exc_value); + Py_XDECREF(f->f_exc_traceback); f->f_back = free_list; free_list = f; } @@ -134,12 +140,13 @@ PyTypeObject PyFrame_Type = { }; PyFrameObject * -PyFrame_New(back, code, globals, locals) - PyFrameObject *back; +PyFrame_New(tstate, code, globals, locals) + PyThreadState *tstate; PyCodeObject *code; PyObject *globals; PyObject *locals; { + PyFrameObject *back = tstate->frame; static PyObject *builtin_object; PyFrameObject *f; PyObject *builtins; @@ -214,6 +221,10 @@ PyFrame_New(back, code, globals, locals) } f->f_locals = locals; f->f_trace = NULL; + f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; + f->f_tstate = PyThreadState_Get(); + if (f->f_tstate == NULL) + Py_FatalError("can't create new frame without thread"); f->f_lasti = 0; f->f_lineno = code->co_firstlineno; diff --git a/Python/Makefile.in b/Python/Makefile.in index c31c640..9fc2de6 100644 --- a/Python/Makefile.in +++ b/Python/Makefile.in @@ -41,7 +41,7 @@ OBJS= \ getplatform.o getversion.o graminit.o \ import.o importdl.o \ marshal.o modsupport.o mystrtoul.o \ - pyfpe.o pythonrun.o \ + pyfpe.o pystate.o pythonrun.o \ sigcheck.o structmember.o sysmodule.o \ traceback.o \ $(LIBOBJS) @@ -107,6 +107,7 @@ memmove.o: memmove.c modsupport.o: modsupport.c mystrtoul.o: mystrtoul.c pyfpe.o: pyfpe.c +pystate.o: pystate.c pythonrun.o: pythonrun.c sigcheck.o: sigcheck.c strerror.o: strerror.c diff --git a/Python/ceval.c b/Python/ceval.c index bca3386..dd40b46 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -47,6 +47,12 @@ PERFORMANCE OF THIS SOFTWARE. #include +#ifdef HAVE_LIMITS_H +#include +#else +#define INT_MAX 2147483647 +#endif + /* Turn this on if your compiler chokes on the big switch: */ /* #define CASE_TOO_BIG 1 */ @@ -68,9 +74,10 @@ static PyObject *eval_code2 Py_PROTO((PyCodeObject *, #ifdef LLTRACE static int prtrace Py_PROTO((PyObject *, char *)); #endif -static void call_exc_trace Py_PROTO((PyObject **, PyObject**, PyFrameObject *)); -static int call_trace - Py_PROTO((PyObject **, PyObject **, PyFrameObject *, char *, PyObject *)); +static void call_exc_trace Py_PROTO((PyObject **, PyObject**, + PyFrameObject *)); +static int call_trace Py_PROTO((PyObject **, PyObject **, + PyFrameObject *, char *, PyObject *)); static PyObject *add Py_PROTO((PyObject *, PyObject *)); static PyObject *sub Py_PROTO((PyObject *, PyObject *)); static PyObject *powerop Py_PROTO((PyObject *, PyObject *)); @@ -90,17 +97,22 @@ static PyObject *call_builtin Py_PROTO((PyObject *, PyObject *, PyObject *)); static PyObject *call_function Py_PROTO((PyObject *, PyObject *, PyObject *)); static PyObject *apply_subscript Py_PROTO((PyObject *, PyObject *)); static PyObject *loop_subscript Py_PROTO((PyObject *, PyObject *)); -static int slice_index Py_PROTO((PyObject *, int, int *)); +static int slice_index Py_PROTO((PyObject *, int *)); static PyObject *apply_slice Py_PROTO((PyObject *, PyObject *, PyObject *)); static int assign_subscript Py_PROTO((PyObject *, PyObject *, PyObject *)); -static int assign_slice Py_PROTO((PyObject *, PyObject *, PyObject *, PyObject *)); +static int assign_slice Py_PROTO((PyObject *, PyObject *, + PyObject *, PyObject *)); static int cmp_exception Py_PROTO((PyObject *, PyObject *)); static int cmp_member Py_PROTO((PyObject *, PyObject *)); static PyObject *cmp_outcome Py_PROTO((int, PyObject *, PyObject *)); static int import_from Py_PROTO((PyObject *, PyObject *, PyObject *)); static PyObject *build_class Py_PROTO((PyObject *, PyObject *, PyObject *)); -static int exec_statement Py_PROTO((PyObject *, PyObject *, PyObject *)); +static int exec_statement Py_PROTO((PyFrameObject *, + PyObject *, PyObject *, PyObject *)); static PyObject *find_from_args Py_PROTO((PyFrameObject *, int)); +static void set_exc_info Py_PROTO((PyThreadState *, + PyObject *, PyObject *, PyObject *)); +static void reset_exc_info Py_PROTO((PyThreadState *)); /* Dynamic execution profile */ @@ -114,15 +126,13 @@ static long dxp[256]; #endif -/* Pointer to current frame, used to link new frames to */ - -static PyFrameObject *current_frame; - #ifdef WITH_THREAD #include #include "thread.h" +extern int _PyThread_Started; /* Flag for Py_Exit */ + static type_lock interpreter_lock = 0; static long main_thread = 0; @@ -131,6 +141,7 @@ PyEval_InitThreads() { if (interpreter_lock) return; + _PyThread_Started = 1; interpreter_lock = allocate_lock(); acquire_lock(interpreter_lock, 1); main_thread = get_thread_ident(); @@ -147,9 +158,8 @@ PyEval_SaveThread() { #ifdef WITH_THREAD if (interpreter_lock) { - PyObject *res; - res = (PyObject *)current_frame; - current_frame = NULL; + PyThreadState *tstate = PyThreadState_Swap(NULL); + PyObject *res = tstate ? (PyObject *) (tstate->frame) : NULL; release_lock(interpreter_lock); return res; } @@ -167,7 +177,7 @@ PyEval_RestoreThread(x) err = errno; acquire_lock(interpreter_lock, 1); errno = err; - current_frame = (PyFrameObject *)x; + PyThreadState_Swap(x ? ((PyFrameObject *)x)->f_tstate : NULL); } #endif } @@ -200,9 +210,10 @@ PyEval_RestoreThread(x) The current code is safe against (2), but not against (1). The safety against (2) is derived from the fact that only one thread (the main thread) ever takes things out of the queue. -*/ -static int ticker = 0; /* main loop counter to do periodic things */ + XXX Darn! With the advent of thread state, we should have an array + of pending calls per thread in the thread state! Later... +*/ #define NPENDINGCALLS 32 static struct { @@ -211,6 +222,7 @@ static struct { } pendingcalls[NPENDINGCALLS]; static volatile int pendingfirst = 0; static volatile int pendinglast = 0; +static volatile int things_to_do = 0; int Py_AddPendingCall(func, arg) @@ -232,7 +244,7 @@ Py_AddPendingCall(func, arg) pendingcalls[i].func = func; pendingcalls[i].arg = arg; pendinglast = j; - ticker = 0; /* Signal main loop */ + things_to_do = 1; /* Signal main loop */ busy = 0; /* XXX End critical section */ return 0; @@ -243,16 +255,13 @@ Py_MakePendingCalls() { static int busy = 0; #ifdef WITH_THREAD - if (main_thread && get_thread_ident() != main_thread) { - ticker = 0; /* We're not done yet */ + if (main_thread && get_thread_ident() != main_thread) return 0; - } #endif - if (busy) { - ticker = 0; /* We're not done yet */ + if (busy) return 0; - } busy = 1; + things_to_do = 0; for (;;) { int i; int (*func) Py_PROTO((ANY *)); @@ -265,7 +274,7 @@ Py_MakePendingCalls() pendingfirst = (i + 1) % NPENDINGCALLS; if (func(arg) < 0) { busy = 0; - ticker = 0; /* We're not done yet */ + things_to_do = 1; /* We're not done yet */ return -1; } } @@ -310,8 +319,6 @@ PyEval_EvalCode(co, globals, locals) #define MAX_RECURSION_DEPTH 10000 #endif -static int recursion_depth = 0; - static PyObject * eval_code2(co, globals, locals, args, argcount, kws, kwcount, defs, defcount, owner) @@ -343,6 +350,7 @@ eval_code2(co, globals, locals, register PyFrameObject *f; /* Current frame */ register PyObject **fastlocals = NULL; PyObject *retval = NULL; /* Return value */ + PyThreadState *tstate = PyThreadState_Get(); #ifdef LLTRACE int lltrace; #endif @@ -385,8 +393,13 @@ eval_code2(co, globals, locals, #define SETLOCAL(i, value) do { Py_XDECREF(GETLOCAL(i)); \ GETLOCAL(i) = value; } while (0) +/* Start of code */ + + if (tstate == NULL) + Py_FatalError("eval_code2 called without a current thread"); + #ifdef USE_STACKCHECK - if (recursion_depth%10 == 0 && PyOS_CheckStack()) { + if (tstate->recursion_depth%10 == 0 && PyOS_CheckStack()) { PyErr_SetString(PyExc_MemoryError, "Stack overflow"); return NULL; } @@ -402,14 +415,14 @@ eval_code2(co, globals, locals, #endif f = PyFrame_New( - current_frame, /*back*/ + tstate, /*back*/ co, /*code*/ globals, /*globals*/ locals); /*locals*/ if (f == NULL) return NULL; - current_frame = f; + tstate->frame = f; fastlocals = f->f_localsplus; if (co->co_argcount > 0 || @@ -428,7 +441,8 @@ eval_code2(co, globals, locals, } if (argcount > co->co_argcount) { if (!(co->co_flags & CO_VARARGS)) { - PyErr_SetString(PyExc_TypeError, "too many arguments"); + PyErr_SetString(PyExc_TypeError, + "too many arguments"); goto fail; } n = co->co_argcount; @@ -455,15 +469,16 @@ eval_code2(co, globals, locals, int j; /* XXX slow -- speed up using dictionary? */ for (j = 0; j < co->co_argcount; j++) { - PyObject *nm = PyTuple_GET_ITEM(co->co_varnames, j); + PyObject *nm = PyTuple_GET_ITEM( + co->co_varnames, j); if (PyObject_Compare(keyword, nm) == 0) break; } if (j >= co->co_argcount) { if (kwdict == NULL) { PyErr_Format(PyExc_TypeError, - "unexpected keyword argument: %.400s", - PyString_AsString(keyword)); + "unexpected keyword argument: %.400s", + PyString_AsString(keyword)); goto fail; } PyDict_SetItem(kwdict, keyword, value); @@ -502,14 +517,15 @@ eval_code2(co, globals, locals, } else { if (argcount > 0 || kwcount > 0) { - PyErr_SetString(PyExc_TypeError, "no arguments expected"); + PyErr_SetString(PyExc_TypeError, + "no arguments expected"); goto fail; } } - if (_PySys_TraceFunc != NULL) { - /* sys_trace, if defined, is a function that will - be called on *every* entry to a code block. + if (tstate->sys_tracefunc != NULL) { + /* tstate->sys_tracefunc, if defined, is a function that + will be called on *every* entry to a code block. Its return value, if not None, is a function that will be called at the start of each executed line of code. (Actually, the function must return @@ -520,26 +536,29 @@ eval_code2(co, globals, locals, depends on the situation. The global trace function (sys.trace) is also called whenever an exception is detected. */ - if (call_trace(&_PySys_TraceFunc, &f->f_trace, f, "call", + if (call_trace(&tstate->sys_tracefunc, + &f->f_trace, f, "call", Py_None/*XXX how to compute arguments now?*/)) { /* Trace function raised an error */ goto fail; } } - if (_PySys_ProfileFunc != NULL) { - /* Similar for sys_profile, except it needn't return + if (tstate->sys_profilefunc != NULL) { + /* Similar for sys_profilefunc, except it needn't return itself and isn't called for "line" events */ - if (call_trace(&_PySys_ProfileFunc, (PyObject**)0, f, "call", + if (call_trace(&tstate->sys_profilefunc, + (PyObject**)0, f, "call", Py_None/*XXX*/)) { goto fail; } } - if (++recursion_depth > MAX_RECURSION_DEPTH) { - --recursion_depth; - PyErr_SetString(PyExc_RuntimeError, "Maximum recursion depth exceeded"); - current_frame = f->f_back; + if (++tstate->recursion_depth > MAX_RECURSION_DEPTH) { + --tstate->recursion_depth; + PyErr_SetString(PyExc_RuntimeError, + "Maximum recursion depth exceeded"); + tstate->frame = f->f_back; Py_DECREF(f); return NULL; } @@ -552,30 +571,27 @@ eval_code2(co, globals, locals, x = Py_None; /* Not a reference, just anything non-NULL */ for (;;) { - /* Do periodic things. - Doing this every time through the loop would add - too much overhead (a function call per instruction). - So we do it only every Nth instruction. - - The ticker is reset to zero if there are pending - calls (see Py_AddPendingCall() and - Py_MakePendingCalls() above). */ - - if (--ticker < 0) { - ticker = _PySys_CheckInterval; - if (pendingfirst != pendinglast) { + /* Do periodic things. Doing this every time through + the loop would add too much overhead, so we do it + only every Nth instruction. We also do it if + ``things_to_do'' is set, i.e. when an asynchronous + event needs attention (e.g. a signal handler or + async I/O handler); see Py_AddPendingCall() and + Py_MakePendingCalls() above. */ + + if (things_to_do || --tstate->ticker < 0) { + tstate->ticker = tstate->sys_checkinterval; + if (things_to_do) { if (Py_MakePendingCalls() < 0) { why = WHY_EXCEPTION; goto on_error; } } -#ifdef macintosh -#undef HAVE_SIGNAL_H -#endif -#ifndef HAVE_SIGNAL_H /* Is this the right #define? */ -/* If we have true signals, the signal handler will call - Py_AddPendingCall() so we don't have to call sigcheck(). - On the Mac and DOS, alas, we have to call it. */ +#if !defined(HAVE_SIGNAL_H) && !defined(macintosh) + /* If we have true signals, the signal handler + will call Py_AddPendingCall() so we don't + have to call sigcheck(). On the Mac and + DOS, alas, we have to call it. */ if (PyErr_CheckSignals()) { why = WHY_EXCEPTION; goto on_error; @@ -586,13 +602,14 @@ eval_code2(co, globals, locals, if (interpreter_lock) { /* Give another thread a chance */ - current_frame = NULL; + PyThreadState *tstate = + PyThreadState_Swap(NULL); release_lock(interpreter_lock); /* Other threads may run now */ acquire_lock(interpreter_lock, 1); - current_frame = f; + PyThreadState_Swap(tstate); } #endif } @@ -919,7 +936,8 @@ eval_code2(co, globals, locals, /* Print value except if procedure result */ /* Before printing, also assign to '_' */ if (v != Py_None && - (err = PyDict_SetItemString(f->f_builtins, "_", v)) == 0 && + (err = PyDict_SetItemString( + f->f_builtins, "_", v)) == 0 && !Py_SuppressPrintingFlag) { Py_FlushLine(); x = PySys_GetObject("stdout"); @@ -952,7 +970,8 @@ eval_code2(co, globals, locals, case PRINT_NEWLINE: x = PySys_GetObject("stdout"); if (x == NULL) - PyErr_SetString(PyExc_RuntimeError, "lost sys.stdout"); + PyErr_SetString(PyExc_RuntimeError, + "lost sys.stdout"); else { PyFile_WriteString("\n", x); PyFile_SoftSpace(x, 0); @@ -986,7 +1005,8 @@ eval_code2(co, globals, locals, case LOAD_LOCALS: if ((x = f->f_locals) == NULL) { - PyErr_SetString(PyExc_SystemError, "no locals"); + PyErr_SetString(PyExc_SystemError, + "no locals"); break; } Py_INCREF(x); @@ -1002,7 +1022,7 @@ eval_code2(co, globals, locals, w = POP(); v = POP(); u = POP(); - err = exec_statement(u, v, w); + err = exec_statement(f, u, v, w); Py_DECREF(u); Py_DECREF(v); Py_DECREF(w); @@ -1055,7 +1075,8 @@ eval_code2(co, globals, locals, w = GETNAMEV(oparg); v = POP(); if ((x = f->f_locals) == NULL) { - PyErr_SetString(PyExc_SystemError, "no locals"); + PyErr_SetString(PyExc_SystemError, + "no locals"); break; } err = PyDict_SetItem(x, w, v); @@ -1065,7 +1086,8 @@ eval_code2(co, globals, locals, case DELETE_NAME: w = GETNAMEV(oparg); if ((x = f->f_locals) == NULL) { - PyErr_SetString(PyExc_SystemError, "no locals"); + PyErr_SetString(PyExc_SystemError, + "no locals"); break; } if ((err = PyDict_DelItem(x, w)) != 0) @@ -1079,7 +1101,8 @@ eval_code2(co, globals, locals, case UNPACK_TUPLE: v = POP(); if (!PyTuple_Check(v)) { - PyErr_SetString(PyExc_TypeError, "unpack non-tuple"); + PyErr_SetString(PyExc_TypeError, + "unpack non-tuple"); why = WHY_EXCEPTION; } else if (PyTuple_Size(v) != oparg) { @@ -1100,7 +1123,8 @@ eval_code2(co, globals, locals, case UNPACK_LIST: v = POP(); if (!PyList_Check(v)) { - PyErr_SetString(PyExc_TypeError, "unpack non-list"); + PyErr_SetString(PyExc_TypeError, + "unpack non-list"); why = WHY_EXCEPTION; } else if (PyList_Size(v) != oparg) { @@ -1130,7 +1154,8 @@ eval_code2(co, globals, locals, case DELETE_ATTR: w = GETNAMEV(oparg); v = POP(); - err = PyObject_SetAttr(v, w, (PyObject *)NULL); /* del v.w */ + err = PyObject_SetAttr(v, w, (PyObject *)NULL); + /* del v.w */ Py_DECREF(v); break; @@ -1156,7 +1181,8 @@ eval_code2(co, globals, locals, case LOAD_NAME: w = GETNAMEV(oparg); if ((x = f->f_locals) == NULL) { - PyErr_SetString(PyExc_SystemError, "no locals"); + PyErr_SetString(PyExc_SystemError, + "no locals"); break; } x = PyDict_GetItem(x, w); @@ -1167,7 +1193,8 @@ eval_code2(co, globals, locals, PyErr_Clear(); x = PyDict_GetItem(f->f_builtins, w); if (x == NULL) { - PyErr_SetObject(PyExc_NameError, w); + PyErr_SetObject( + PyExc_NameError, w); break; } } @@ -1286,7 +1313,8 @@ eval_code2(co, globals, locals, w = Py_BuildValue("(OOOO)", w, f->f_globals, - f->f_locals == NULL ? Py_None : f->f_locals, + f->f_locals == NULL ? + Py_None : f->f_locals, u); Py_DECREF(u); if (w == NULL) { @@ -1304,7 +1332,8 @@ eval_code2(co, globals, locals, v = TOP(); PyFrame_FastToLocals(f); if ((x = f->f_locals) == NULL) { - PyErr_SetString(PyExc_SystemError, "no locals"); + PyErr_SetString(PyExc_SystemError, + "no locals"); break; } err = import_from(x, v, w); @@ -1432,7 +1461,8 @@ eval_code2(co, globals, locals, class)) /* Handy-dandy */ ; else { - PyErr_SetString(PyExc_TypeError, + PyErr_SetString( + PyExc_TypeError, "unbound method must be called with class instance 1st argument"); x = NULL; break; @@ -1443,13 +1473,16 @@ eval_code2(co, globals, locals, Py_INCREF(func); if (PyFunction_Check(func)) { PyObject *co = PyFunction_GetCode(func); - PyObject *globals = PyFunction_GetGlobals(func); - PyObject *argdefs = PyFunction_GetDefaults(func); + PyObject *globals = + PyFunction_GetGlobals(func); + PyObject *argdefs = + PyFunction_GetDefaults(func); PyObject **d; int nd; if (argdefs != NULL) { d = &PyTuple_GET_ITEM(argdefs, 0); - nd = ((PyTupleObject *)argdefs)->ob_size; + nd = ((PyTupleObject *)argdefs) -> + ob_size; } else { d = NULL; @@ -1589,7 +1622,8 @@ eval_code2(co, globals, locals, if (why == WHY_EXCEPTION || why == WHY_RERAISE) { if (!PyErr_Occurred()) { fprintf(stderr, "XXX ghost error\n"); - PyErr_SetString(PyExc_SystemError, "ghost error"); + PyErr_SetString(PyExc_SystemError, + "ghost error"); why = WHY_EXCEPTION; } } @@ -1613,9 +1647,10 @@ eval_code2(co, globals, locals, if (f->f_trace) call_exc_trace(&f->f_trace, &f->f_trace, f); - if (_PySys_ProfileFunc) - call_exc_trace(&_PySys_ProfileFunc, (PyObject**)0, f); - } + if (tstate->sys_profilefunc) + call_exc_trace(&tstate->sys_profilefunc, + (PyObject**)0, f); +} /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */ @@ -1651,9 +1686,8 @@ eval_code2(co, globals, locals, Python main loop. Don't do this for 'finally'. */ if (b->b_type == SETUP_EXCEPT) { - PySys_SetObject("exc_traceback", tb); - PySys_SetObject("exc_value", val); - PySys_SetObject("exc_type", exc); + set_exc_info(tstate, + exc, val, tb); } PUSH(tb); PUSH(val); @@ -1699,8 +1733,8 @@ eval_code2(co, globals, locals, } } - if (_PySys_ProfileFunc && why == WHY_RETURN) { - if (call_trace(&_PySys_ProfileFunc, (PyObject**)0, + if (tstate->sys_profilefunc && why == WHY_RETURN) { + if (call_trace(&tstate->sys_profilefunc, (PyObject**)0, f, "return", retval)) { Py_XDECREF(retval); retval = NULL; @@ -1708,18 +1742,89 @@ eval_code2(co, globals, locals, } } - --recursion_depth; + reset_exc_info(tstate); + + --tstate->recursion_depth; fail: /* Jump here from prelude on failure */ /* Restore previous frame and release the current one */ - current_frame = f->f_back; + tstate->frame = f->f_back; Py_DECREF(f); return retval; } +static void +set_exc_info(tstate, type, value, tb) + PyThreadState *tstate; + PyObject *type; + PyObject *value; + PyObject *tb; +{ + PyFrameObject *frame; + frame = tstate->frame; + if (frame->f_exc_type == NULL) { + /* This frame didn't catch an exception before */ + /* Save previous exception of this thread in this frame */ + Py_XDECREF(frame->f_exc_type); + Py_XDECREF(frame->f_exc_value); + Py_XDECREF(frame->f_exc_traceback); + if (tstate->exc_type == NULL) { + Py_INCREF(Py_None); + tstate->exc_type = Py_None; + } + Py_XINCREF(tstate->exc_type); + Py_XINCREF(tstate->exc_value); + Py_XINCREF(tstate->exc_traceback); + frame->f_exc_type = tstate->exc_type; + frame->f_exc_value = tstate->exc_value; + frame->f_exc_traceback = tstate->exc_traceback; + } + /* Set new exception for this thread */ + Py_XINCREF(type); + Py_XINCREF(value); + Py_XINCREF(tb); + tstate->exc_type = type; + tstate->exc_value = value; + tstate->exc_traceback = tb; + /* For b/w compatibility */ + PySys_SetObject("exc_type", type); + PySys_SetObject("exc_value", value); + PySys_SetObject("exc_traceback", tb); +} + +static void +reset_exc_info(tstate) + PyThreadState *tstate; +{ + PyFrameObject *frame; + frame = tstate->frame; + if (frame->f_exc_type != NULL) { + /* This frame caught an exception */ + Py_XDECREF(tstate->exc_type); + Py_XDECREF(tstate->exc_value); + Py_XDECREF(tstate->exc_traceback); + Py_XINCREF(frame->f_exc_type); + Py_XINCREF(frame->f_exc_value); + Py_XINCREF(frame->f_exc_traceback); + tstate->exc_type = frame->f_exc_type; + tstate->exc_value = frame->f_exc_value; + tstate->exc_traceback = frame->f_exc_traceback; + /* For b/w compatibility */ + PySys_SetObject("exc_type", frame->f_exc_type); + PySys_SetObject("exc_value", frame->f_exc_value); + PySys_SetObject("exc_traceback", frame->f_exc_traceback); + } + Py_XDECREF(frame->f_exc_type); + Py_XDECREF(frame->f_exc_value); + Py_XDECREF(frame->f_exc_traceback); + frame->f_exc_type = NULL; + frame->f_exc_value = NULL; + frame->f_exc_traceback = NULL; +} + /* Logic for the raise statement (too complicated for inlining). This *consumes* a reference count to each of its arguments. */ static enum why_code @@ -1895,11 +2000,11 @@ call_trace(p_trace, p_newtrace, f, msg, arg) char *msg; PyObject *arg; { + PyThreadState *tstate = f->f_tstate; PyObject *args, *what; PyObject *res = NULL; - static int tracing = 0; - if (tracing) { + if (tstate->tracing) { /* Don't do recursive traces */ if (p_newtrace) { Py_XDECREF(*p_newtrace); @@ -1921,11 +2026,11 @@ call_trace(p_trace, p_newtrace, f, msg, arg) arg = Py_None; Py_INCREF(arg); PyTuple_SET_ITEM(args, 2, arg); - tracing++; + tstate->tracing++; PyFrame_FastToLocals(f); res = PyEval_CallObject(*p_trace, args); /* May clear *p_trace! */ PyFrame_LocalsToFast(f, 1); - tracing--; + tstate->tracing--; Py_Cleanup: Py_XDECREF(args); if (res == NULL) { @@ -1957,6 +2062,7 @@ call_trace(p_trace, p_newtrace, f, msg, arg) PyObject * PyEval_GetBuiltins() { + PyFrameObject *current_frame = PyThreadState_Get()->frame; if (current_frame == NULL) return PyBuiltin_GetModule(); else @@ -1966,6 +2072,7 @@ PyEval_GetBuiltins() PyObject * PyEval_GetLocals() { + PyFrameObject *current_frame = PyThreadState_Get()->frame; if (current_frame == NULL) return NULL; PyFrame_FastToLocals(current_frame); @@ -1975,6 +2082,7 @@ PyEval_GetLocals() PyObject * PyEval_GetGlobals() { + PyFrameObject *current_frame = PyThreadState_Get()->frame; if (current_frame == NULL) return NULL; else @@ -1984,12 +2092,14 @@ PyEval_GetGlobals() PyObject * PyEval_GetFrame() { + PyFrameObject *current_frame = PyThreadState_Get()->frame; return (PyObject *)current_frame; } int PyEval_GetRestricted() { + PyFrameObject *current_frame = PyThreadState_Get()->frame; return current_frame == NULL ? 0 : current_frame->f_restricted; } @@ -2002,6 +2112,9 @@ Py_FlushLine() } +/* XXX Many of the following operator implementations could be + replaced by calls into the 'abstract' module. */ + #define BINOP(opname, ropname, thisfunc) \ if (!PyInstance_Check(v) && !PyInstance_Check(w)) \ ; \ @@ -2245,7 +2358,8 @@ powerop(v, w) BINOP("__pow__", "__rpow__", powerop); if (v->ob_type->tp_as_number == NULL || w->ob_type->tp_as_number == NULL) { - PyErr_SetString(PyExc_TypeError, "pow() requires numeric arguments"); + PyErr_SetString(PyExc_TypeError, + "pow() requires numeric arguments"); return NULL; } if (PyNumber_Coerce(&v, &w) != 0) @@ -2328,14 +2442,16 @@ PyEval_CallObjectWithKeywords(func, arg, kw) if (arg == NULL) arg = PyTuple_New(0); else if (!PyTuple_Check(arg)) { - PyErr_SetString(PyExc_TypeError, "argument list must be a tuple"); + PyErr_SetString(PyExc_TypeError, + "argument list must be a tuple"); return NULL; } else Py_INCREF(arg); if (kw != NULL && !PyDict_Check(kw)) { - PyErr_SetString(PyExc_TypeError, "keyword list must be a dictionary"); + PyErr_SetString(PyExc_TypeError, + "keyword list must be a dictionary"); return NULL; } @@ -2460,7 +2576,8 @@ call_function(func, arg, kw) } else { if (!PyFunction_Check(func)) { - PyErr_SetString(PyExc_TypeError, "call of non-function"); + PyErr_SetString(PyExc_TypeError, + "call of non-function"); return NULL; } Py_INCREF(arg); @@ -2529,7 +2646,8 @@ apply_subscript(v, w) int i; if (!PyInt_Check(w)) { if (PySlice_Check(w)) { - PyErr_SetString(PyExc_ValueError, SLICE_ERROR_MSG); + PyErr_SetString(PyExc_ValueError, + SLICE_ERROR_MSG); } else { PyErr_SetString(PyExc_TypeError, "sequence subscript not int"); @@ -2567,19 +2685,24 @@ loop_subscript(v, w) } static int -slice_index(v, isize, pi) +slice_index(v, pi) PyObject *v; - int isize; int *pi; { if (v != NULL) { + long x; if (!PyInt_Check(v)) { - PyErr_SetString(PyExc_TypeError, "slice index must be int"); + PyErr_SetString(PyExc_TypeError, + "slice index must be int"); return -1; } - *pi = PyInt_AsLong(v); - if (*pi < 0) - *pi += isize; + x = PyInt_AsLong(v); + /* Truncate -- very long indices are truncated anyway */ + if (x > INT_MAX) + x = INT_MAX; + else if (x < -INT_MAX) + x = 0; + *pi = x; } return 0; } @@ -2588,21 +2711,12 @@ static PyObject * apply_slice(u, v, w) /* return u[v:w] */ PyObject *u, *v, *w; { - PyTypeObject *tp = u->ob_type; - int ilow, ihigh, isize; - if (tp->tp_as_sequence == NULL) { - PyErr_SetString(PyExc_TypeError, "only sequences can be sliced"); + int ilow = 0, ihigh = INT_MAX; + if (slice_index(v, &ilow) != 0) return NULL; - } - ilow = 0; - isize = ihigh = (*tp->tp_as_sequence->sq_length)(u); - if (isize < 0) + if (slice_index(w, &ihigh) != 0) return NULL; - if (slice_index(v, isize, &ilow) != 0) - return NULL; - if (slice_index(w, isize, &ihigh) != 0) - return NULL; - return (*tp->tp_as_sequence->sq_slice)(u, ilow, ihigh); + return PySequence_GetSlice(u, ilow, ihigh); } static int @@ -2649,25 +2763,15 @@ static int assign_slice(u, v, w, x) /* u[v:w] = x */ PyObject *u, *v, *w, *x; { - PySequenceMethods *sq = u->ob_type->tp_as_sequence; - int ilow, ihigh, isize; - if (sq == NULL) { - PyErr_SetString(PyExc_TypeError, "assign to slice of non-sequence"); - return -1; - } - if (sq == NULL || sq->sq_ass_slice == NULL) { - PyErr_SetString(PyExc_TypeError, "unassignable slice"); - return -1; - } - ilow = 0; - isize = ihigh = (*sq->sq_length)(u); - if (isize < 0) + int ilow = 0, ihigh = INT_MAX; + if (slice_index(v, &ilow) != 0) return -1; - if (slice_index(v, isize, &ilow) != 0) + if (slice_index(w, &ihigh) != 0) return -1; - if (slice_index(w, isize, &ihigh) != 0) - return -1; - return (*sq->sq_ass_slice)(u, ilow, ihigh, x); + if (x == NULL) + return PySequence_DelSlice(u, ilow, ihigh); + else + return PySequence_SetSlice(u, ilow, ihigh, x); } static int @@ -2788,7 +2892,8 @@ import_from(locals, v, name) { PyObject *w, *x; if (!PyModule_Check(v)) { - PyErr_SetString(PyExc_TypeError, "import-from requires module object"); + PyErr_SetString(PyExc_TypeError, + "import-from requires module object"); return -1; } w = PyModule_GetDict(v); @@ -2830,15 +2935,18 @@ build_class(methods, bases, name) { int i; if (!PyTuple_Check(bases)) { - PyErr_SetString(PyExc_SystemError, "build_class with non-tuple bases"); + PyErr_SetString(PyExc_SystemError, + "build_class with non-tuple bases"); return NULL; } if (!PyDict_Check(methods)) { - PyErr_SetString(PyExc_SystemError, "build_class with non-dictionary"); + PyErr_SetString(PyExc_SystemError, + "build_class with non-dictionary"); return NULL; } if (!PyString_Check(name)) { - PyErr_SetString(PyExc_SystemError, "build_class witn non-string name"); + PyErr_SetString(PyExc_SystemError, + "build_class witn non-string name"); return NULL; } for (i = PyTuple_Size(bases); --i >= 0; ) { @@ -2852,9 +2960,10 @@ build_class(methods, bases, name) if (base->ob_type->ob_type->tp_call) { PyObject *args; PyObject *class; - args = Py_BuildValue("(OOO)", name, bases, methods); - class = PyEval_CallObject((PyObject *)base->ob_type, - args); + args = Py_BuildValue("(OOO)", + name, bases, methods); + class = PyEval_CallObject( + (PyObject *)base->ob_type, args); Py_DECREF(args); return class; } @@ -2867,7 +2976,8 @@ build_class(methods, bases, name) } static int -exec_statement(prog, globals, locals) +exec_statement(f, prog, globals, locals) + PyFrameObject *f; PyObject *prog; PyObject *globals; PyObject *locals; @@ -2907,9 +3017,10 @@ exec_statement(prog, globals, locals) return -1; } if (PyDict_GetItemString(globals, "__builtins__") == NULL) - PyDict_SetItemString(globals, "__builtins__", current_frame->f_builtins); + PyDict_SetItemString(globals, "__builtins__", f->f_builtins); if (PyCode_Check(prog)) { - if (PyEval_EvalCode((PyCodeObject *) prog, globals, locals) == NULL) + if (PyEval_EvalCode((PyCodeObject *) prog, + globals, locals) == NULL) return -1; return 0; } @@ -2922,7 +3033,8 @@ exec_statement(prog, globals, locals) } s = PyString_AsString(prog); if (strlen(s) != PyString_Size(prog)) { - PyErr_SetString(PyExc_ValueError, "embedded '\\0' in exec string"); + PyErr_SetString(PyExc_ValueError, + "embedded '\\0' in exec string"); return -1; } v = PyRun_String(s, file_input, globals, locals); @@ -2930,7 +3042,7 @@ exec_statement(prog, globals, locals) return -1; Py_DECREF(v); if (plain) - PyFrame_LocalsToFast(current_frame, 0); + PyFrame_LocalsToFast(f, 0); return 0; } diff --git a/Python/errors.c b/Python/errors.c index d654b76..4795aaf 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -55,23 +55,34 @@ extern char *strerror Py_PROTO((int)); #endif #endif -/* Last exception stored */ - -static PyObject *last_exception; -static PyObject *last_exc_val; - void -PyErr_Restore(exception, value, traceback) - PyObject *exception; +PyErr_Restore(type, value, traceback) + PyObject *type; PyObject *value; PyObject *traceback; { - PyErr_Clear(); + PyThreadState *tstate = PyThreadState_Get(); + PyObject *oldtype, *oldvalue, *oldtraceback; + + if (traceback != NULL && !PyTraceBack_Check(traceback)) { + /* XXX Should never happen -- fatal error instead? */ + Py_DECREF(traceback); + traceback = NULL; + } - last_exception = exception; - last_exc_val = value; - (void) PyTraceBack_Store(traceback); - Py_XDECREF(traceback); + /* Save these in locals to safeguard against recursive + invocation through Py_XDECREF */ + oldtype = tstate->curexc_type; + oldvalue = tstate->curexc_value; + oldtraceback = tstate->curexc_traceback; + + tstate->curexc_type = type; + tstate->curexc_value = value; + tstate->curexc_traceback = traceback; + + Py_XDECREF(oldtype); + Py_XDECREF(oldvalue); + Py_XDECREF(oldtraceback); } void @@ -105,33 +116,32 @@ PyErr_SetString(exception, string) PyObject * PyErr_Occurred() { - return last_exception; + PyThreadState *tstate = PyThreadState_Get(); + + return tstate->curexc_type; } void -PyErr_Fetch(p_exc, p_val, p_tb) - PyObject **p_exc; - PyObject **p_val; - PyObject **p_tb; +PyErr_Fetch(p_type, p_value, p_traceback) + PyObject **p_type; + PyObject **p_value; + PyObject **p_traceback; { - *p_exc = last_exception; - last_exception = NULL; - *p_val = last_exc_val; - last_exc_val = NULL; - *p_tb = PyTraceBack_Fetch(); + PyThreadState *tstate = PyThreadState_Get(); + + *p_type = tstate->curexc_type; + *p_value = tstate->curexc_value; + *p_traceback = tstate->curexc_traceback; + + tstate->curexc_type = NULL; + tstate->curexc_value = NULL; + tstate->curexc_traceback = NULL; } void PyErr_Clear() { - PyObject *tb; - Py_XDECREF(last_exception); - last_exception = NULL; - Py_XDECREF(last_exc_val); - last_exc_val = NULL; - /* Also clear interpreter stack trace */ - tb = PyTraceBack_Fetch(); - Py_XDECREF(tb); + PyErr_Restore(NULL, NULL, NULL); } /* Convenience functions to set a type error exception and return 0 */ @@ -139,7 +149,8 @@ PyErr_Clear() int PyErr_BadArgument() { - PyErr_SetString(PyExc_TypeError, "illegal argument type for built-in operation"); + PyErr_SetString(PyExc_TypeError, + "illegal argument type for built-in operation"); return 0; } @@ -171,7 +182,8 @@ PyErr_SetFromErrno(exc) void PyErr_BadInternalCall() { - PyErr_SetString(PyExc_SystemError, "bad argument to internal function"); + PyErr_SetString(PyExc_SystemError, + "bad argument to internal function"); } diff --git a/Python/pystate.c b/Python/pystate.c new file mode 100644 index 0000000..7ce499a --- /dev/null +++ b/Python/pystate.c @@ -0,0 +1,163 @@ +/*********************************************************** +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Thread and interpreter state structures and their interfaces */ + +#include "Python.h" + + +static PyThreadState *current_tstate = NULL; + + +PyInterpreterState * +PyInterpreterState_New() +{ + PyInterpreterState *interp = PyMem_NEW(PyInterpreterState, 1); + if (interp != NULL) { + interp->import_modules = NULL; + interp->sysdict = NULL; + interp->nthreads = 0; + interp->nexitfuncs = 0; + } + return interp; +} + + +void +PyInterpreterState_Delete(PyInterpreterState *interp) +{ + Py_XDECREF(interp->import_modules); + Py_XDECREF(interp->sysdict); + + PyMem_DEL(interp); +} + + +PyThreadState * +PyThreadState_New(PyInterpreterState *interp) +{ + PyThreadState *tstate = PyMem_NEW(PyThreadState, 1); + /* fprintf(stderr, "new tstate -> %p\n", tstate); */ + if (tstate != NULL) { + tstate->interpreter_state = interp; + + tstate->frame = NULL; + tstate->recursion_depth = 0; + tstate->ticker = 0; + tstate->tracing = 0; + + tstate->curexc_type = NULL; + tstate->curexc_value = NULL; + tstate->curexc_traceback = NULL; + + tstate->exc_type = NULL; + tstate->exc_value = NULL; + tstate->exc_traceback = NULL; + + tstate->sys_profilefunc = NULL; + tstate->sys_tracefunc = NULL; + tstate->sys_checkinterval = 0; + + interp->nthreads++; + } + return tstate; +} + + +void +PyThreadState_Delete(PyThreadState *tstate) +{ + /* fprintf(stderr, "delete tstate %p\n", tstate); */ + if (tstate == current_tstate) + current_tstate = NULL; + tstate->interpreter_state->nthreads--; + + Py_XDECREF((PyObject *) (tstate->frame)); /* XXX really? */ + + Py_XDECREF(tstate->curexc_type); + Py_XDECREF(tstate->curexc_value); + Py_XDECREF(tstate->curexc_traceback); + + Py_XDECREF(tstate->exc_type); + Py_XDECREF(tstate->exc_value); + Py_XDECREF(tstate->exc_traceback); + + Py_XDECREF(tstate->sys_profilefunc); + Py_XDECREF(tstate->sys_tracefunc); + + PyMem_DEL(tstate); +} + + +PyThreadState * +PyThreadState_Get() +{ + /* fprintf(stderr, "get tstate -> %p\n", current_tstate); */ + return current_tstate; +} + + +PyThreadState * +PyThreadState_Swap(PyThreadState *new) +{ + PyThreadState *old = current_tstate; + /* fprintf(stderr, "swap tstate new=%p <--> old=%p\n", new, old); */ + current_tstate = new; + return old; +} + + +/* How should one use this code? + + 1. Standard Python interpreter, assuming no other interpreters or threads: + + PyInterpreterState *interp; + PyThreadState *tstate; + interp = PyInterpreterState_New(); + if (interp == NULL) + Py_FatalError("..."); + tstate = PyThreadState_New(interp); + if (tstate == NULL) + Py_FatalError("..."); + (void) PyThreadState_Swap(tstate); + PyInitialize(); + . + . (use the interpreter here) + . + Py_Cleanup(); + PyThreadState_Delete(tstate); + PyInterpreterState_Delete(interp); + + 2. Totally indepent interpreter invocation in a separate C thread: + + XXX Need to interact with the thread lock nevertheless!!! + +*/ diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 4d6b918..f08a2c4 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -77,17 +77,11 @@ int Py_VerboseFlag; /* Needed by import.c */ int Py_SuppressPrintingFlag; /* Needed by ceval.c */ int Py_InteractiveFlag; /* Needed by Py_FdIsInteractive() below */ -/* Initialize all */ +/* Initialize the current interpreter; pass in the Python path. */ void -Py_Initialize() +Py_Setup() { - static int inited; - - if (inited) - return; - inited = 1; - PyImport_Init(); /* Modules '__builtin__' and 'sys' are initialized here, @@ -105,6 +99,46 @@ Py_Initialize() initmain(); } +/* Create and interpreter and thread state and initialize them; + if we already have an interpreter and thread, do nothing. + Fatal error if the creation fails. */ + +void +Py_Initialize() +{ + PyThreadState *tstate; + PyInterpreterState *interp; + if (PyThreadState_Get()) + return; + interp = PyInterpreterState_New(); + if (interp == NULL) + Py_FatalError("PyInterpreterState_New() failed"); + tstate = PyThreadState_New(interp); + if (tstate == NULL) + Py_FatalError("PyThreadState_New() failed"); + (void) PyThreadState_Swap(tstate); + + Py_Setup(); + + PySys_SetPath(Py_GetPath()); +} + +/* + Py_Initialize() + -- do everything, no-op on second call, call fatal on failure, set path + + #2 + -- create new interp+tstate & make it current, return NULL on failure, + make it current, do all setup, set path + + #3 + -- #2 without set path + + #4 + -- is there any point to #3 for caller-provided current interp+tstate? + +*/ + /* Create __main__ module */ static void diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 830fb5d..b758344 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -48,9 +48,6 @@ Data members: #include "osdefs.h" -PyObject *_PySys_TraceFunc, *_PySys_ProfileFunc; -int _PySys_CheckInterval = 10; - #if HAVE_UNISTD_H #include #endif @@ -98,6 +95,25 @@ PySys_SetObject(name, v) } static PyObject * +sys_exc_info(self, args) + PyObject *self; + PyObject *args; +{ + PyThreadState *tstate; + if (!PyArg_Parse(args, "")) + return NULL; + tstate = PyThreadState_Get(); + if (tstate == NULL) + Py_FatalError("sys.exc_info(): no thread state"); + return Py_BuildValue( + "(OOO)", + tstate->exc_type != NULL ? tstate->exc_type : Py_None, + tstate->exc_value != NULL ? tstate->exc_value : Py_None, + tstate->exc_traceback != NULL ? + tstate->exc_traceback : Py_None); +} + +static PyObject * sys_exit(self, args) PyObject *self; PyObject *args; @@ -112,12 +128,13 @@ sys_settrace(self, args) PyObject *self; PyObject *args; { + PyThreadState *tstate = PyThreadState_Get(); if (args == Py_None) args = NULL; else Py_XINCREF(args); - Py_XDECREF(_PySys_TraceFunc); - _PySys_TraceFunc = args; + Py_XDECREF(tstate->sys_tracefunc); + tstate->sys_tracefunc = args; Py_INCREF(Py_None); return Py_None; } @@ -127,12 +144,13 @@ sys_setprofile(self, args) PyObject *self; PyObject *args; { + PyThreadState *tstate = PyThreadState_Get(); if (args == Py_None) args = NULL; else Py_XINCREF(args); - Py_XDECREF(_PySys_ProfileFunc); - _PySys_ProfileFunc = args; + Py_XDECREF(tstate->sys_profilefunc); + tstate->sys_profilefunc = args; Py_INCREF(Py_None); return Py_None; } @@ -142,7 +160,8 @@ sys_setcheckinterval(self, args) PyObject *self; PyObject *args; { - if (!PyArg_ParseTuple(args, "i", &_PySys_CheckInterval)) + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, "i", &tstate->sys_checkinterval)) return NULL; Py_INCREF(Py_None); return Py_None; @@ -202,6 +221,7 @@ extern PyObject *_Py_GetDXProfile Py_PROTO((PyObject *, PyObject *)); static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ + {"exc_info", sys_exc_info, 0}, {"exit", sys_exit, 0}, #ifdef COUNT_ALLOCS {"getcounts", sys_getcounts, 0}, @@ -232,7 +252,8 @@ list_builtin_module_names() if (list == NULL) return NULL; for (i = 0; _PyImport_Inittab[i].name != NULL; i++) { - PyObject *name = PyString_FromString(_PyImport_Inittab[i].name); + PyObject *name = PyString_FromString( + _PyImport_Inittab[i].name); if (name == NULL) break; PyList_Append(list, name); diff --git a/Python/thread_sgi.h b/Python/thread_sgi.h index 32b94f2..a53c8dc 100644 --- a/Python/thread_sgi.h +++ b/Python/thread_sgi.h @@ -451,3 +451,83 @@ void up_sema _P1(sema, type_sema sema) if (usvsema((usema_t *) sema) < 0) perror("usvsema"); } + +/* + * Per-thread data ("key") support. + */ + +struct key { + struct key *next; + long id; + int key; + void *value; +}; + +static struct key *keyhead = NULL; +static int nkeys = 0; +static type_lock keymutex = NULL; + +static struct key *find_key _P2(key, int key, value, void *value) +{ + struct key *p; + long id = get_thread_ident(); + for (p = keyhead; p != NULL; p = p->next) { + if (p->id == id && p->key == key) + return p; + } + if (value == NULL) + return NULL; + p = (struct key *)malloc(sizeof(struct key)); + if (p != NULL) { + p->id = id; + p->key = key; + p->value = value; + acquire_lock(keymutex, 1); + p->next = keyhead; + keyhead = p; + release_lock(keymutex); + } + return p; +} + +int create_key _P0() +{ + if (keymutex == NULL) + keymutex = allocate_lock(); + return ++nkeys; +} + +void delete_key _P1(key, int key) +{ + struct key *p, **q; + acquire_lock(keymutex, 1); + q = &keyhead; + while ((p = *q) != NULL) { + if (p->key == key) { + *q = p->next; + free((void *)p); + /* NB This does *not* free p->value! */ + } + else + q = &p->next; + } + release_lock(keymutex); +} + +int set_key_value _P2(key, int key, value, void *value) +{ + struct key *p = find_key(key, value); + if (p == NULL) + return -1; + else + return 0; +} + +void *get_key_value _P1(key, int key) +{ + struct key *p = find_key(key, NULL); + if (p == NULL) + return NULL; + else + return p->value; +} diff --git a/Python/traceback.c b/Python/traceback.c index 14e4905..06fcc53 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -117,42 +117,18 @@ newtracebackobject(next, frame, lasti, lineno) return tb; } -static tracebackobject *tb_current = NULL; - int PyTraceBack_Here(frame) PyFrameObject *frame; { - tracebackobject *tb; - tb = newtracebackobject(tb_current, frame, - frame->f_lasti, frame->f_lineno); + PyThreadState *tstate = frame->f_tstate; + tracebackobject *oldtb = (tracebackobject *) tstate->curexc_traceback; + tracebackobject *tb = newtracebackobject(oldtb, + frame, frame->f_lasti, frame->f_lineno); if (tb == NULL) return -1; - Py_XDECREF(tb_current); - tb_current = tb; - return 0; -} - -PyObject * -PyTraceBack_Fetch() -{ - PyObject *v; - v = (PyObject *)tb_current; - tb_current = NULL; - return v; -} - -int -PyTraceBack_Store(v) - PyObject *v; -{ - if (v != NULL && !is_tracebackobject(v)) { - PyErr_BadInternalCall(); - return -1; - } - Py_XDECREF(tb_current); - Py_XINCREF(v); - tb_current = (tracebackobject *)v; + tstate->curexc_traceback = (PyObject *)tb; + Py_XDECREF(oldtb); return 0; } -- cgit v0.12