Skip to content

Commit a027efa

Browse files
committed
Massive changes for separate thread state management.
All per-thread globals are moved into a struct which is manipulated separately.
1 parent 73237c5 commit a027efa

File tree

15 files changed

+864
-261
lines changed

15 files changed

+864
-261
lines changed

Include/Python.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ PERFORMANCE OF THIS SOFTWARE.
9595
#include "import.h"
9696
#include "bltinmodule.h"
9797

98+
#include "pystate.h"
99+
98100
#include "abstract.h"
99101

100102
#define PyArg_GetInt(v, a) PyArg_Parse((v), "i", (a))

Include/frameobject.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ typedef struct _frame {
5252
PyObject *f_locals; /* local symbol table (PyDictObject) */
5353
PyObject **f_valuestack; /* points after the last local */
5454
PyObject *f_trace; /* Trace function */
55+
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
56+
PyThreadState *f_tstate;
5557
int f_lasti; /* Last instruction if called */
5658
int f_lineno; /* Current line number */
5759
int f_restricted; /* Flag set if restricted operations
@@ -71,7 +73,7 @@ extern DL_IMPORT(PyTypeObject) PyFrame_Type;
7173
#define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type)
7274

7375
PyFrameObject * PyFrame_New
74-
Py_PROTO((PyFrameObject *, PyCodeObject *,
76+
Py_PROTO((PyThreadState *, PyCodeObject *,
7577
PyObject *, PyObject *));
7678

7779

Include/pystate.h

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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 */

Include/pythread.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ extern "C" {
3535
#define up_sema PyThread_up_sema
3636
#define exit_prog PyThread_exit_prog
3737
#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
3842

3943

4044
void init_thread Py_PROTO((void));
@@ -62,6 +66,11 @@ void exit_prog Py_PROTO((int));
6266
void _exit_prog Py_PROTO((int));
6367
#endif
6468

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+
6574
#ifdef __cplusplus
6675
}
6776
#endif

Include/thread.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ extern "C" {
3535
#define up_sema PyThread_up_sema
3636
#define exit_prog PyThread_exit_prog
3737
#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
3842

3943

4044
void init_thread Py_PROTO((void));
@@ -62,6 +66,11 @@ void exit_prog Py_PROTO((int));
6266
void _exit_prog Py_PROTO((int));
6367
#endif
6468

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+
6574
#ifdef __cplusplus
6675
}
6776
#endif

Modules/threadmodule.c

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,13 @@ PERFORMANCE OF THIS SOFTWARE.
3535
#include "Python.h"
3636

3737
#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'."
4041
#endif
4142

4243
#include "thread.h"
4344

44-
extern int _PyThread_Started;
45-
4645
static PyObject *ThreadError;
4746

4847

@@ -192,52 +191,90 @@ static PyTypeObject Locktype = {
192191

193192
/* Module functions */
194193

194+
struct bootstate {
195+
PyInterpreterState *interp;
196+
PyObject *func;
197+
PyObject *args;
198+
PyObject *keyw;
199+
};
200+
195201
static void
196-
t_bootstrap(args_raw)
197-
void *args_raw;
202+
t_bootstrap(boot_raw)
203+
void *boot_raw;
198204
{
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;
203208

209+
tstate = PyThreadState_New(boot->interp);
204210
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);
209218
if (res == NULL) {
210219
if (PyErr_Occurred() == PyExc_SystemExit)
211220
PyErr_Clear();
212221
else {
213222
fprintf(stderr, "Unhandled exception in thread:\n");
214-
PyErr_Print(); /* From pythonmain.c */
223+
PyErr_Print();
215224
}
216225
}
217226
else
218227
Py_DECREF(res);
219-
(void) PyEval_SaveThread(); /* Should always be NULL */
228+
(void) PyThreadState_Swap(alttstate);
229+
(void) PyEval_SaveThread();
230+
PyThreadState_Delete(tstate);
220231
exit_thread();
221232
}
222233

223234
static PyObject *
224-
thread_start_new_thread(self, args)
235+
thread_start_new_thread(self, fargs)
225236
PyObject *self; /* Not used */
226-
PyObject *args;
237+
PyObject *fargs;
227238
{
228-
PyObject *func, *arg;
239+
PyObject *func, *args = NULL, *keyw = NULL;
240+
struct bootstate *boot;
229241

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");
231247
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);
232267
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)) {
237271
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);
238276
return NULL;
239277
}
240-
/* Otherwise the DECREF(args) is done by t_bootstrap */
241278
Py_INCREF(Py_None);
242279
return Py_None;
243280
}
@@ -294,8 +331,8 @@ thread_get_ident(self, args)
294331
}
295332

296333
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},
299336
{"allocate_lock", (PyCFunction)thread_allocate_lock},
300337
{"allocate", (PyCFunction)thread_allocate_lock},
301338
{"exit_thread", (PyCFunction)thread_exit_thread},

Objects/frameobject.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ static struct memberlist frame_memberlist[] = {
5050
{"f_lineno", T_INT, OFF(f_lineno), RO},
5151
{"f_restricted",T_INT, OFF(f_restricted),RO},
5252
{"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)},
5356
{NULL} /* Sentinel */
5457
};
5558

@@ -112,6 +115,9 @@ frame_dealloc(f)
112115
Py_XDECREF(f->f_globals);
113116
Py_XDECREF(f->f_locals);
114117
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);
115121
f->f_back = free_list;
116122
free_list = f;
117123
}
@@ -134,12 +140,13 @@ PyTypeObject PyFrame_Type = {
134140
};
135141

136142
PyFrameObject *
137-
PyFrame_New(back, code, globals, locals)
138-
PyFrameObject *back;
143+
PyFrame_New(tstate, code, globals, locals)
144+
PyThreadState *tstate;
139145
PyCodeObject *code;
140146
PyObject *globals;
141147
PyObject *locals;
142148
{
149+
PyFrameObject *back = tstate->frame;
143150
static PyObject *builtin_object;
144151
PyFrameObject *f;
145152
PyObject *builtins;
@@ -214,6 +221,10 @@ PyFrame_New(back, code, globals, locals)
214221
}
215222
f->f_locals = locals;
216223
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");
217228

218229
f->f_lasti = 0;
219230
f->f_lineno = code->co_firstlineno;

0 commit comments

Comments
 (0)