GeezerVison is showing github.com — Massive changes for separate thread state management. · python/cpython@a027efa
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Include/Python.h +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -95,6 +95,8 @@ PERFORMANCE OF THIS SOFTWARE. | 95 | 95 | #include "import.h" | 96 | 96 | #include "bltinmodule.h" | 97 | 97 |
| | 98 | +#include "pystate.h" | | 99 | + | 98 | 100 | #include "abstract.h" | 99 | 101 |
| 100 | 102 | #define PyArg_GetInt(v, a) PyArg_Parse((v), "i", (a)) | |
Collapse file Copy file name to clipboardExpand all lines: Include/frameobject.h +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -52,6 +52,8 @@ typedef struct _frame { | 52 | 52 | PyObject *f_locals; /* local symbol table (PyDictObject) */ | 53 | 53 | PyObject **f_valuestack; /* points after the last local */ | 54 | 54 | PyObject *f_trace; /* Trace function */ | | 55 | + PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; | | 56 | + PyThreadState *f_tstate; | 55 | 57 | int f_lasti; /* Last instruction if called */ | 56 | 58 | int f_lineno; /* Current line number */ | 57 | 59 | int f_restricted; /* Flag set if restricted operations | @@ -71,7 +73,7 @@ extern DL_IMPORT(PyTypeObject) PyFrame_Type; | 71 | 73 | #define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type) | 72 | 74 |
| 73 | 75 | PyFrameObject * PyFrame_New | 74 | | - Py_PROTO((PyFrameObject *, PyCodeObject *, | | 76 | + Py_PROTO((PyThreadState *, PyCodeObject *, | 75 | 77 | PyObject *, PyObject *)); | 76 | 78 |
| 77 | 79 |
| |
Collapse file Copy file name to clipboard +134Lines changed: 134 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +#ifndef Py_PYSTATE_H | | 2 | +#define Py_PYSTATE_H | | 3 | +#ifdef __cplusplus | | 4 | +extern "C" { | | 5 | +#endif | | 6 | + | | 7 | +/*********************************************************** | | 8 | +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, | | 9 | +The Netherlands. | | 10 | +
| | 11 | + All Rights Reserved | | 12 | +
| | 13 | +Permission to use, copy, modify, and distribute this software and its | | 14 | +documentation for any purpose and without fee is hereby granted, | | 15 | +provided that the above copyright notice appear in all copies and that | | 16 | +both that copyright notice and this permission notice appear in | | 17 | +supporting documentation, and that the names of Stichting Mathematisch | | 18 | +Centrum or CWI or Corporation for National Research Initiatives or | | 19 | +CNRI not be used in advertising or publicity pertaining to | | 20 | +distribution of the software without specific, written prior | | 21 | +permission. | | 22 | +
| | 23 | +While CWI is the initial source for this software, a modified version | | 24 | +is made available by the Corporation for National Research Initiatives | | 25 | +(CNRI) at the Internet address ftp://ftp.python.org. | | 26 | +
| | 27 | +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH | | 28 | +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF | | 29 | +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH | | 30 | +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL | | 31 | +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | | 32 | +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | | 33 | +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | | 34 | +PERFORMANCE OF THIS SOFTWARE. | | 35 | +
| | 36 | +******************************************************************/ | | 37 | + | | 38 | +/* Thread and interpreter state structures and their interfaces */ | | 39 | + | | 40 | + | | 41 | +/* State shared between threads */ | | 42 | + | | 43 | +#define NEXITFUNCS 32 | | 44 | + | | 45 | +typedef struct _is { | | 46 | + | | 47 | + PyObject *import_modules; | | 48 | + PyObject *sysdict; | | 49 | + | | 50 | + int nthreads; | | 51 | + | | 52 | + void (*exitfuncs[NEXITFUNCS])(); | | 53 | + int nexitfuncs; | | 54 | + | | 55 | +} PyInterpreterState; | | 56 | + | | 57 | + | | 58 | +/* State unique per thread */ | | 59 | + | | 60 | +struct _frame; /* Avoid including frameobject.h */ | | 61 | + | | 62 | +typedef struct _ts { | | 63 | + | | 64 | + PyInterpreterState *interpreter_state; | | 65 | + | | 66 | + struct _frame *frame; | | 67 | + int recursion_depth; | | 68 | + int ticker; | | 69 | + int tracing; | | 70 | + | | 71 | + PyObject *sys_profilefunc; | | 72 | + PyObject *sys_tracefunc; | | 73 | + int sys_checkinterval; | | 74 | + | | 75 | + PyObject *curexc_type; | | 76 | + PyObject *curexc_value; | | 77 | + PyObject *curexc_traceback; | | 78 | + | | 79 | + PyObject *exc_type; | | 80 | + PyObject *exc_value; | | 81 | + PyObject *exc_traceback; | | 82 | + | | 83 | + /* XXX Other state that should be here: | | 84 | + - signal handlers | | 85 | + - low-level "pending calls" | | 86 | + Problem with both is that they may be referenced from | | 87 | + interrupt handlers where there is no clear concept of a | | 88 | + "current thread"??? | | 89 | + */ | | 90 | + | | 91 | +} PyThreadState; | | 92 | + | | 93 | + | | 94 | +PyInterpreterState *PyInterpreterState_New(void); | | 95 | +void PyInterpreterState_Delete(PyInterpreterState *); | | 96 | + | | 97 | +PyThreadState *PyThreadState_New(PyInterpreterState *); | | 98 | +void PyThreadState_Delete(PyThreadState *); | | 99 | + | | 100 | +PyThreadState *PyThreadState_Get(void); | | 101 | +PyThreadState *PyThreadState_Swap(PyThreadState *); | | 102 | + | | 103 | +/* Some background. | | 104 | +
| | 105 | + There are lots of issues here. | | 106 | +
| | 107 | + First, we can build Python without threads, with threads, or (when | | 108 | + Greg Stein's mods are out of beta, on some platforms) with free | | 109 | + threading. | | 110 | +
| | 111 | + Next, assuming some form of threading is used, there can be several | | 112 | + kinds of threads. Python code can create threads with the thread | | 113 | + module. C code can create threads with the interface defined in | | 114 | + python's "thread.h". Or C code can create threads directly with | | 115 | + the OS threads interface (e.g. Solaris threads, SGI threads or | | 116 | + pthreads, whatever is being used, as long as it's the same that | | 117 | + Python is configured for). | | 118 | +
| | 119 | + Next, let's discuss sharing of interpreter state between threads. | | 120 | + The exception state (sys.exc_* currently) should never be shared | | 121 | + between threads, because it is stack frame specific. The contents | | 122 | + of the sys module, in particular sys.modules and sys.path, are | | 123 | + generally shared between threads. But occasionally it is useful to | | 124 | + have separate module collections, e.g. when threads originate in C | | 125 | + code and are used to execute unrelated Python scripts. | | 126 | + (Traditionally, one would use separate processes for this, but | | 127 | + there are lots of reasons why threads are attractive.) | | 128 | +
| | 129 | +*/ | | 130 | + | | 131 | +#ifdef __cplusplus | | 132 | +} | | 133 | +#endif | | 134 | +#endif /* !Py_PYSTATE_H */ |
Collapse file Copy file name to clipboardExpand all lines: Include/pythread.h +9Lines changed: 9 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -35,6 +35,10 @@ extern "C" { | 35 | 35 | #define up_sema PyThread_up_sema | 36 | 36 | #define exit_prog PyThread_exit_prog | 37 | 37 | #define _exit_prog PyThread__exit_prog | | 38 | +#define create_key PyThread_create_key | | 39 | +#define delete_key PyThread_delete_key | | 40 | +#define get_key_value PyThread_get_key_value | | 41 | +#define set_key_value PyThread_set_key_value | 38 | 42 |
| 39 | 43 |
| 40 | 44 | void init_thread Py_PROTO((void)); | @@ -62,6 +66,11 @@ void exit_prog Py_PROTO((int)); | 62 | 66 | void _exit_prog Py_PROTO((int)); | 63 | 67 | #endif | 64 | 68 |
| | 69 | +int create_key Py_PROTO((void)); | | 70 | +void delete_key Py_PROTO((int)); | | 71 | +int set_key_value Py_PROTO((int, void *)); | | 72 | +void * get_key_value Py_PROTO((int)); | | 73 | + | 65 | 74 | #ifdef __cplusplus | 66 | 75 | } | 67 | 76 | #endif | |
Collapse file Copy file name to clipboardExpand all lines: Include/thread.h +9Lines changed: 9 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -35,6 +35,10 @@ extern "C" { | 35 | 35 | #define up_sema PyThread_up_sema | 36 | 36 | #define exit_prog PyThread_exit_prog | 37 | 37 | #define _exit_prog PyThread__exit_prog | | 38 | +#define create_key PyThread_create_key | | 39 | +#define delete_key PyThread_delete_key | | 40 | +#define get_key_value PyThread_get_key_value | | 41 | +#define set_key_value PyThread_set_key_value | 38 | 42 |
| 39 | 43 |
| 40 | 44 | void init_thread Py_PROTO((void)); | @@ -62,6 +66,11 @@ void exit_prog Py_PROTO((int)); | 62 | 66 | void _exit_prog Py_PROTO((int)); | 63 | 67 | #endif | 64 | 68 |
| | 69 | +int create_key Py_PROTO((void)); | | 70 | +void delete_key Py_PROTO((int)); | | 71 | +int set_key_value Py_PROTO((int, void *)); | | 72 | +void * get_key_value Py_PROTO((int)); | | 73 | + | 65 | 74 | #ifdef __cplusplus | 66 | 75 | } | 67 | 76 | #endif | |
Collapse file Copy file name to clipboardExpand all lines: Modules/threadmodule.c +64-27Lines changed: 64 additions & 27 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -35,14 +35,13 @@ PERFORMANCE OF THIS SOFTWARE. | 35 | 35 | #include "Python.h" | 36 | 36 |
| 37 | 37 | #ifndef WITH_THREAD | 38 | | -Error! The rest of Python is not compiled with thread support. | 39 | | -Rerun configure, adding a --with-thread option. | | 38 | +#error "Error! The rest of Python is not compiled with thread support." | | 39 | +#error "Rerun configure, adding a --with-thread option." | | 40 | +#error "Then run `make clean' followed by `make'." | 40 | 41 | #endif | 41 | 42 |
| 42 | 43 | #include "thread.h" | 43 | 44 |
| 44 | | -extern int _PyThread_Started; | 45 | | - | 46 | 45 | static PyObject *ThreadError; | 47 | 46 |
| 48 | 47 |
| @@ -192,52 +191,90 @@ static PyTypeObject Locktype = { | 192 | 191 |
| 193 | 192 | /* Module functions */ | 194 | 193 |
| | 194 | +struct bootstate { | | 195 | + PyInterpreterState *interp; | | 196 | + PyObject *func; | | 197 | + PyObject *args; | | 198 | + PyObject *keyw; | | 199 | +}; | | 200 | + | 195 | 201 | static void | 196 | | -t_bootstrap(args_raw) | 197 | | - void *args_raw; | | 202 | +t_bootstrap(boot_raw) | | 203 | + void *boot_raw; | 198 | 204 | { | 199 | | - PyObject *args = (PyObject *) args_raw; | 200 | | - PyObject *func, *arg, *res; | 201 | | - | 202 | | - _PyThread_Started++; | | 205 | + struct bootstate *boot = (struct bootstate *) boot_raw; | | 206 | + PyThreadState *alttstate, *tstate; | | 207 | + PyObject *res; | 203 | 208 |
| | 209 | + tstate = PyThreadState_New(boot->interp); | 204 | 210 | PyEval_RestoreThread((void *)NULL); | 205 | | - func = PyTuple_GetItem(args, 0); | 206 | | - arg = PyTuple_GetItem(args, 1); | 207 | | - res = PyEval_CallObject(func, arg); | 208 | | - Py_DECREF(args); /* Matches the INCREF(args) in thread_start_new_thread */ | | 211 | + alttstate = PyThreadState_Swap(tstate); | | 212 | + res = PyEval_CallObjectWithKeywords( | | 213 | + boot->func, boot->args, boot->keyw); | | 214 | + Py_DECREF(boot->func); | | 215 | + Py_DECREF(boot->args); | | 216 | + Py_XDECREF(boot->keyw); | | 217 | + PyMem_DEL(boot_raw); | 209 | 218 | if (res == NULL) { | 210 | 219 | if (PyErr_Occurred() == PyExc_SystemExit) | 211 | 220 | PyErr_Clear(); | 212 | 221 | else { | 213 | 222 | fprintf(stderr, "Unhandled exception in thread:\n"); | 214 | | - PyErr_Print(); /* From pythonmain.c */ | | 223 | + PyErr_Print(); | 215 | 224 | } | 216 | 225 | } | 217 | 226 | else | 218 | 227 | Py_DECREF(res); | 219 | | - (void) PyEval_SaveThread(); /* Should always be NULL */ | | 228 | + (void) PyThreadState_Swap(alttstate); | | 229 | + (void) PyEval_SaveThread(); | | 230 | + PyThreadState_Delete(tstate); | 220 | 231 | exit_thread(); | 221 | 232 | } | 222 | 233 |
| 223 | 234 | static PyObject * | 224 | | -thread_start_new_thread(self, args) | | 235 | +thread_start_new_thread(self, fargs) | 225 | 236 | PyObject *self; /* Not used */ | 226 | | - PyObject *args; | | 237 | + PyObject *fargs; | 227 | 238 | { | 228 | | - PyObject *func, *arg; | | 239 | + PyObject *func, *args = NULL, *keyw = NULL; | | 240 | + struct bootstate *boot; | 229 | 241 |
| 230 | | - if (!PyArg_Parse(args, "(OO)", &func, &arg)) | | 242 | + if (!PyArg_ParseTuple(fargs, "OO|O", &func, &args, &keyw)) | | 243 | + return NULL; | | 244 | + if (!PyCallable_Check(func)) { | | 245 | + PyErr_SetString(PyExc_TypeError, | | 246 | + "first arg must be callable"); | 231 | 247 | return NULL; | | 248 | + } | | 249 | + if (!PyTuple_Check(args)) { | | 250 | + PyErr_SetString(PyExc_TypeError, | | 251 | + "optional 2nd arg must be a tuple"); | | 252 | + return NULL; | | 253 | + } | | 254 | + if (keyw != NULL && !PyDict_Check(keyw)) { | | 255 | + PyErr_SetString(PyExc_TypeError, | | 256 | + "optional 3rd arg must be a dictionary"); | | 257 | + return NULL; | | 258 | + } | | 259 | + boot = PyMem_NEW(struct bootstate, 1); | | 260 | + if (boot == NULL) | | 261 | + return PyErr_NoMemory(); | | 262 | + boot->interp = PyThreadState_Get()->interpreter_state; | | 263 | + boot->func = func; | | 264 | + boot->args = args; | | 265 | + boot->keyw = keyw; | | 266 | + Py_INCREF(func); | 232 | 267 | Py_INCREF(args); | 233 | | - /* Initialize the interpreter's stack save/restore mechanism */ | 234 | | - PyEval_InitThreads(); | 235 | | - if (!start_new_thread(t_bootstrap, (void*) args)) { | 236 | | - Py_DECREF(args); | | 268 | + Py_XINCREF(keyw); | | 269 | + PyEval_InitThreads(); /* Start the interpreter's thread-awareness */ | | 270 | + if (!start_new_thread(t_bootstrap, (void*) boot)) { | 237 | 271 | PyErr_SetString(ThreadError, "can't start new thread\n"); | | 272 | + Py_DECREF(func); | | 273 | + Py_DECREF(args); | | 274 | + Py_XDECREF(keyw); | | 275 | + PyMem_DEL(boot); | 238 | 276 | return NULL; | 239 | 277 | } | 240 | | - /* Otherwise the DECREF(args) is done by t_bootstrap */ | 241 | 278 | Py_INCREF(Py_None); | 242 | 279 | return Py_None; | 243 | 280 | } | @@ -294,8 +331,8 @@ thread_get_ident(self, args) | 294 | 331 | } | 295 | 332 |
| 296 | 333 | static PyMethodDef thread_methods[] = { | 297 | | - {"start_new_thread", (PyCFunction)thread_start_new_thread}, | 298 | | - {"start_new", (PyCFunction)thread_start_new_thread}, | | 334 | + {"start_new_thread", (PyCFunction)thread_start_new_thread, 1}, | | 335 | + {"start_new", (PyCFunction)thread_start_new_thread, 1}, | 299 | 336 | {"allocate_lock", (PyCFunction)thread_allocate_lock}, | 300 | 337 | {"allocate", (PyCFunction)thread_allocate_lock}, | 301 | 338 | {"exit_thread", (PyCFunction)thread_exit_thread}, | |
Collapse file Copy file name to clipboardExpand all lines: Objects/frameobject.c +13-2Lines changed: 13 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -50,6 +50,9 @@ static struct memberlist frame_memberlist[] = { | 50 | 50 | {"f_lineno", T_INT, OFF(f_lineno), RO}, | 51 | 51 | {"f_restricted",T_INT, OFF(f_restricted),RO}, | 52 | 52 | {"f_trace", T_OBJECT, OFF(f_trace)}, | | 53 | + {"f_exc_type", T_OBJECT, OFF(f_exc_type)}, | | 54 | + {"f_exc_value", T_OBJECT, OFF(f_exc_value)}, | | 55 | + {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, | 53 | 56 | {NULL} /* Sentinel */ | 54 | 57 | }; | 55 | 58 |
| @@ -112,6 +115,9 @@ frame_dealloc(f) | 112 | 115 | Py_XDECREF(f->f_globals); | 113 | 116 | Py_XDECREF(f->f_locals); | 114 | 117 | Py_XDECREF(f->f_trace); | | 118 | + Py_XDECREF(f->f_exc_type); | | 119 | + Py_XDECREF(f->f_exc_value); | | 120 | + Py_XDECREF(f->f_exc_traceback); | 115 | 121 | f->f_back = free_list; | 116 | 122 | free_list = f; | 117 | 123 | } | @@ -134,12 +140,13 @@ PyTypeObject PyFrame_Type = { | 134 | 140 | }; | 135 | 141 |
| 136 | 142 | PyFrameObject * | 137 | | -PyFrame_New(back, code, globals, locals) | 138 | | - PyFrameObject *back; | | 143 | +PyFrame_New(tstate, code, globals, locals) | | 144 | + PyThreadState *tstate; | 139 | 145 | PyCodeObject *code; | 140 | 146 | PyObject *globals; | 141 | 147 | PyObject *locals; | 142 | 148 | { | | 149 | + PyFrameObject *back = tstate->frame; | 143 | 150 | static PyObject *builtin_object; | 144 | 151 | PyFrameObject *f; | 145 | 152 | PyObject *builtins; | @@ -214,6 +221,10 @@ PyFrame_New(back, code, globals, locals) | 214 | 221 | } | 215 | 222 | f->f_locals = locals; | 216 | 223 | f->f_trace = NULL; | | 224 | + f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; | | 225 | + f->f_tstate = PyThreadState_Get(); | | 226 | + if (f->f_tstate == NULL) | | 227 | + Py_FatalError("can't create new frame without thread"); | 217 | 228 |
| 218 | 229 | f->f_lasti = 0; | 219 | 230 | f->f_lineno = code->co_firstlineno; | |
|