bpo-30860: Consolidate stateful runtime globals. (#2594)

* group the (stateful) runtime globals into various topical structs
* consolidate the topical structs under a single top-level _PyRuntimeState struct
* add a check-c-globals.py script that helps identify runtime globals

Other globals are excluded (see globals.txt and check-c-globals.py).
This commit is contained in:
Eric Snow 2017-09-05 18:26:16 -07:00 committed by GitHub
parent 501b324d3a
commit 76d5abc868
40 changed files with 2727 additions and 1327 deletions

View file

@ -8,13 +8,6 @@ PyDoc_STRVAR(warnings__doc__,
MODULE_NAME " provides basic warning filtering support.\n"
"It is a helper module to speed up interpreter start-up.");
/* Both 'filters' and 'onceregistry' can be set in warnings.py;
get_warnings_attr() will reset these variables accordingly. */
static PyObject *_filters; /* List */
static PyObject *_once_registry; /* Dict */
static PyObject *_default_action; /* String */
static long _filters_version;
_Py_IDENTIFIER(argv);
_Py_IDENTIFIER(stderr);
@ -53,7 +46,7 @@ get_warnings_attr(const char *attr, int try_import)
}
/* don't try to import after the start of the Python finallization */
if (try_import && _Py_Finalizing == NULL) {
if (try_import && !_Py_IS_FINALIZING()) {
warnings_module = PyImport_Import(warnings_str);
if (warnings_module == NULL) {
/* Fallback to the C implementation if we cannot get
@ -90,10 +83,10 @@ get_once_registry(void)
if (registry == NULL) {
if (PyErr_Occurred())
return NULL;
return _once_registry;
return _PyRuntime.warnings.once_registry;
}
Py_DECREF(_once_registry);
_once_registry = registry;
Py_DECREF(_PyRuntime.warnings.once_registry);
_PyRuntime.warnings.once_registry = registry;
return registry;
}
@ -108,11 +101,11 @@ get_default_action(void)
if (PyErr_Occurred()) {
return NULL;
}
return _default_action;
return _PyRuntime.warnings.default_action;
}
Py_DECREF(_default_action);
_default_action = default_action;
Py_DECREF(_PyRuntime.warnings.default_action);
_PyRuntime.warnings.default_action = default_action;
return default_action;
}
@ -132,23 +125,24 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
return NULL;
}
else {
Py_DECREF(_filters);
_filters = warnings_filters;
Py_DECREF(_PyRuntime.warnings.filters);
_PyRuntime.warnings.filters = warnings_filters;
}
if (_filters == NULL || !PyList_Check(_filters)) {
PyObject *filters = _PyRuntime.warnings.filters;
if (filters == NULL || !PyList_Check(filters)) {
PyErr_SetString(PyExc_ValueError,
MODULE_NAME ".filters must be a list");
return NULL;
}
/* _filters could change while we are iterating over it. */
for (i = 0; i < PyList_GET_SIZE(_filters); i++) {
/* _PyRuntime.warnings.filters could change while we are iterating over it. */
for (i = 0; i < PyList_GET_SIZE(filters); i++) {
PyObject *tmp_item, *action, *msg, *cat, *mod, *ln_obj;
Py_ssize_t ln;
int is_subclass, good_msg, good_mod;
tmp_item = PyList_GET_ITEM(_filters, i);
tmp_item = PyList_GET_ITEM(filters, i);
if (!PyTuple_Check(tmp_item) || PyTuple_GET_SIZE(tmp_item) != 5) {
PyErr_Format(PyExc_ValueError,
MODULE_NAME ".filters item %zd isn't a 5-tuple", i);
@ -220,9 +214,9 @@ already_warned(PyObject *registry, PyObject *key, int should_set)
version_obj = _PyDict_GetItemId(registry, &PyId_version);
if (version_obj == NULL
|| !PyLong_CheckExact(version_obj)
|| PyLong_AsLong(version_obj) != _filters_version) {
|| PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version) {
PyDict_Clear(registry);
version_obj = PyLong_FromLong(_filters_version);
version_obj = PyLong_FromLong(_PyRuntime.warnings.filters_version);
if (version_obj == NULL)
return -1;
if (_PyDict_SetItemId(registry, &PyId_version, version_obj) < 0) {
@ -520,7 +514,7 @@ warn_explicit(PyObject *category, PyObject *message,
if (registry == NULL)
goto cleanup;
}
/* _once_registry[(text, category)] = 1 */
/* _PyRuntime.warnings.once_registry[(text, category)] = 1 */
rc = update_registry(registry, text, category, 0);
}
else if (_PyUnicode_EqualToASCIIString(action, "module")) {
@ -910,7 +904,7 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
static PyObject *
warnings_filters_mutated(PyObject *self, PyObject *args)
{
_filters_version++;
_PyRuntime.warnings.filters_version++;
Py_RETURN_NONE;
}
@ -1160,7 +1154,8 @@ create_filter(PyObject *category, const char *action)
}
/* This assumes the line number is zero for now. */
return PyTuple_Pack(5, action_obj, Py_None, category, Py_None, _PyLong_Zero);
return PyTuple_Pack(5, action_obj, Py_None,
category, Py_None, _PyLong_Zero);
}
static PyObject *
@ -1228,33 +1223,35 @@ _PyWarnings_Init(void)
if (m == NULL)
return NULL;
if (_filters == NULL) {
_filters = init_filters();
if (_filters == NULL)
if (_PyRuntime.warnings.filters == NULL) {
_PyRuntime.warnings.filters = init_filters();
if (_PyRuntime.warnings.filters == NULL)
return NULL;
}
Py_INCREF(_filters);
if (PyModule_AddObject(m, "filters", _filters) < 0)
Py_INCREF(_PyRuntime.warnings.filters);
if (PyModule_AddObject(m, "filters", _PyRuntime.warnings.filters) < 0)
return NULL;
if (_once_registry == NULL) {
_once_registry = PyDict_New();
if (_once_registry == NULL)
if (_PyRuntime.warnings.once_registry == NULL) {
_PyRuntime.warnings.once_registry = PyDict_New();
if (_PyRuntime.warnings.once_registry == NULL)
return NULL;
}
Py_INCREF(_once_registry);
if (PyModule_AddObject(m, "_onceregistry", _once_registry) < 0)
Py_INCREF(_PyRuntime.warnings.once_registry);
if (PyModule_AddObject(m, "_onceregistry",
_PyRuntime.warnings.once_registry) < 0)
return NULL;
if (_default_action == NULL) {
_default_action = PyUnicode_FromString("default");
if (_default_action == NULL)
if (_PyRuntime.warnings.default_action == NULL) {
_PyRuntime.warnings.default_action = PyUnicode_FromString("default");
if (_PyRuntime.warnings.default_action == NULL)
return NULL;
}
Py_INCREF(_default_action);
if (PyModule_AddObject(m, "_defaultaction", _default_action) < 0)
Py_INCREF(_PyRuntime.warnings.default_action);
if (PyModule_AddObject(m, "_defaultaction",
_PyRuntime.warnings.default_action) < 0)
return NULL;
_filters_version = 0;
_PyRuntime.warnings.filters_version = 0;
return m;
}

View file

@ -36,7 +36,8 @@ extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **);
typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *);
/* Forward declarations */
Py_LOCAL_INLINE(PyObject *) call_function(PyObject ***, Py_ssize_t, PyObject *);
Py_LOCAL_INLINE(PyObject *) call_function(PyObject ***, Py_ssize_t,
PyObject *);
static PyObject * do_call_core(PyObject *, PyObject *, PyObject *);
#ifdef LLTRACE
@ -52,13 +53,15 @@ static int call_trace_protected(Py_tracefunc, PyObject *,
static void call_exc_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *);
static int maybe_call_line_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *, int *, int *, int *);
PyThreadState *, PyFrameObject *,
int *, int *, int *);
static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *);
static void dtrace_function_entry(PyFrameObject *);
static void dtrace_function_return(PyFrameObject *);
static PyObject * cmp_outcome(int, PyObject *, PyObject *);
static PyObject * import_name(PyFrameObject *, PyObject *, PyObject *, PyObject *);
static PyObject * import_name(PyFrameObject *, PyObject *, PyObject *,
PyObject *);
static PyObject * import_from(PyObject *, PyObject *);
static int import_all_from(PyObject *, PyObject *);
static void format_exc_check_arg(PyObject *, const char *, PyObject *);
@ -88,7 +91,7 @@ static long dxp[256];
#endif
#ifdef WITH_THREAD
#define GIL_REQUEST _Py_atomic_load_relaxed(&gil_drop_request)
#define GIL_REQUEST _Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request)
#else
#define GIL_REQUEST 0
#endif
@ -98,22 +101,22 @@ static long dxp[256];
the GIL eventually anyway. */
#define COMPUTE_EVAL_BREAKER() \
_Py_atomic_store_relaxed( \
&eval_breaker, \
&_PyRuntime.ceval.eval_breaker, \
GIL_REQUEST | \
_Py_atomic_load_relaxed(&pendingcalls_to_do) | \
pending_async_exc)
_Py_atomic_load_relaxed(&_PyRuntime.ceval.pending.calls_to_do) | \
_PyRuntime.ceval.pending.async_exc)
#ifdef WITH_THREAD
#define SET_GIL_DROP_REQUEST() \
do { \
_Py_atomic_store_relaxed(&gil_drop_request, 1); \
_Py_atomic_store_relaxed(&eval_breaker, 1); \
_Py_atomic_store_relaxed(&_PyRuntime.ceval.gil_drop_request, 1); \
_Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \
} while (0)
#define RESET_GIL_DROP_REQUEST() \
do { \
_Py_atomic_store_relaxed(&gil_drop_request, 0); \
_Py_atomic_store_relaxed(&_PyRuntime.ceval.gil_drop_request, 0); \
COMPUTE_EVAL_BREAKER(); \
} while (0)
@ -122,47 +125,35 @@ static long dxp[256];
/* Pending calls are only modified under pending_lock */
#define SIGNAL_PENDING_CALLS() \
do { \
_Py_atomic_store_relaxed(&pendingcalls_to_do, 1); \
_Py_atomic_store_relaxed(&eval_breaker, 1); \
_Py_atomic_store_relaxed(&_PyRuntime.ceval.pending.calls_to_do, 1); \
_Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \
} while (0)
#define UNSIGNAL_PENDING_CALLS() \
do { \
_Py_atomic_store_relaxed(&pendingcalls_to_do, 0); \
_Py_atomic_store_relaxed(&_PyRuntime.ceval.pending.calls_to_do, 0); \
COMPUTE_EVAL_BREAKER(); \
} while (0)
#define SIGNAL_ASYNC_EXC() \
do { \
pending_async_exc = 1; \
_Py_atomic_store_relaxed(&eval_breaker, 1); \
_PyRuntime.ceval.pending.async_exc = 1; \
_Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \
} while (0)
#define UNSIGNAL_ASYNC_EXC() \
do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0)
do { \
_PyRuntime.ceval.pending.async_exc = 0; \
COMPUTE_EVAL_BREAKER(); \
} while (0)
/* This single variable consolidates all requests to break out of the fast path
in the eval loop. */
static _Py_atomic_int eval_breaker = {0};
/* Request for running pending calls. */
static _Py_atomic_int pendingcalls_to_do = {0};
/* Request for looking at the `async_exc` field of the current thread state.
Guarded by the GIL. */
static int pending_async_exc = 0;
#ifdef WITH_THREAD
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include "pythread.h"
static PyThread_type_lock pending_lock = 0; /* for pending calls */
static unsigned long main_thread = 0;
/* Request for dropping the GIL */
static _Py_atomic_int gil_drop_request = {0};
#include "ceval_gil.h"
int
@ -178,9 +169,9 @@ PyEval_InitThreads(void)
return;
create_gil();
take_gil(PyThreadState_GET());
main_thread = PyThread_get_thread_ident();
if (!pending_lock)
pending_lock = PyThread_allocate_lock();
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
if (!_PyRuntime.ceval.pending.lock)
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
}
void
@ -248,9 +239,9 @@ PyEval_ReInitThreads(void)
if (!gil_created())
return;
recreate_gil();
pending_lock = PyThread_allocate_lock();
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
take_gil(current_tstate);
main_thread = PyThread_get_thread_ident();
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
/* Destroy all threads except the current one */
_PyThreadState_DeleteExcept(current_tstate);
@ -294,7 +285,7 @@ PyEval_RestoreThread(PyThreadState *tstate)
int err = errno;
take_gil(tstate);
/* _Py_Finalizing is protected by the GIL */
if (_Py_Finalizing && tstate != _Py_Finalizing) {
if (_Py_IS_FINALIZING() && !_Py_CURRENTLY_FINALIZING(tstate)) {
drop_gil(tstate);
PyThread_exit_thread();
assert(0); /* unreachable */
@ -346,19 +337,11 @@ _PyEval_SignalReceived(void)
callback.
*/
#define NPENDINGCALLS 32
static struct {
int (*func)(void *);
void *arg;
} pendingcalls[NPENDINGCALLS];
static int pendingfirst = 0;
static int pendinglast = 0;
int
Py_AddPendingCall(int (*func)(void *), void *arg)
{
int i, j, result=0;
PyThread_type_lock lock = pending_lock;
PyThread_type_lock lock = _PyRuntime.ceval.pending.lock;
/* try a few times for the lock. Since this mechanism is used
* for signal handling (on the main thread), there is a (slim)
@ -380,14 +363,14 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
return -1;
}
i = pendinglast;
i = _PyRuntime.ceval.pending.last;
j = (i + 1) % NPENDINGCALLS;
if (j == pendingfirst) {
if (j == _PyRuntime.ceval.pending.first) {
result = -1; /* Queue full */
} else {
pendingcalls[i].func = func;
pendingcalls[i].arg = arg;
pendinglast = j;
_PyRuntime.ceval.pending.calls[i].func = func;
_PyRuntime.ceval.pending.calls[i].arg = arg;
_PyRuntime.ceval.pending.last = j;
}
/* signal main loop */
SIGNAL_PENDING_CALLS();
@ -405,16 +388,19 @@ Py_MakePendingCalls(void)
assert(PyGILState_Check());
if (!pending_lock) {
if (!_PyRuntime.ceval.pending.lock) {
/* initial allocation of the lock */
pending_lock = PyThread_allocate_lock();
if (pending_lock == NULL)
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
if (_PyRuntime.ceval.pending.lock == NULL)
return -1;
}
/* only service pending calls on main thread */
if (main_thread && PyThread_get_thread_ident() != main_thread)
if (_PyRuntime.ceval.pending.main_thread &&
PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
{
return 0;
}
/* don't perform recursive pending calls */
if (busy)
return 0;
@ -436,16 +422,16 @@ Py_MakePendingCalls(void)
void *arg = NULL;
/* pop one item off the queue while holding the lock */
PyThread_acquire_lock(pending_lock, WAIT_LOCK);
j = pendingfirst;
if (j == pendinglast) {
PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
j = _PyRuntime.ceval.pending.first;
if (j == _PyRuntime.ceval.pending.last) {
func = NULL; /* Queue empty */
} else {
func = pendingcalls[j].func;
arg = pendingcalls[j].arg;
pendingfirst = (j + 1) % NPENDINGCALLS;
func = _PyRuntime.ceval.pending.calls[j].func;
arg = _PyRuntime.ceval.pending.calls[j].arg;
_PyRuntime.ceval.pending.first = (j + 1) % NPENDINGCALLS;
}
PyThread_release_lock(pending_lock);
PyThread_release_lock(_PyRuntime.ceval.pending.lock);
/* having released the lock, perform the callback */
if (func == NULL)
break;
@ -489,14 +475,6 @@ error:
The two threads could theoretically wiggle around the "busy" variable.
*/
#define NPENDINGCALLS 32
static struct {
int (*func)(void *);
void *arg;
} pendingcalls[NPENDINGCALLS];
static volatile int pendingfirst = 0;
static volatile int pendinglast = 0;
int
Py_AddPendingCall(int (*func)(void *), void *arg)
{
@ -506,15 +484,15 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
if (busy)
return -1;
busy = 1;
i = pendinglast;
i = _PyRuntime.ceval.pending.last;
j = (i + 1) % NPENDINGCALLS;
if (j == pendingfirst) {
if (j == _PyRuntime.ceval.pending.first) {
busy = 0;
return -1; /* Queue full */
}
pendingcalls[i].func = func;
pendingcalls[i].arg = arg;
pendinglast = j;
_PyRuntime.ceval.pending.calls[i].func = func;
_PyRuntime.ceval.pending.calls[i].arg = arg;
_PyRuntime.ceval.pending.last = j;
SIGNAL_PENDING_CALLS();
busy = 0;
@ -543,12 +521,12 @@ Py_MakePendingCalls(void)
int i;
int (*func)(void *);
void *arg;
i = pendingfirst;
if (i == pendinglast)
i = _PyRuntime.ceval.pending.first;
if (i == _PyRuntime.ceval.pending.last)
break; /* Queue empty */
func = pendingcalls[i].func;
arg = pendingcalls[i].arg;
pendingfirst = (i + 1) % NPENDINGCALLS;
func = _PyRuntime.ceval.pending.calls[i].func;
arg = _PyRuntime.ceval.pending.calls[i].arg;
_PyRuntime.ceval.pending.first = (i + 1) % NPENDINGCALLS;
if (func(arg) < 0) {
goto error;
}
@ -570,20 +548,32 @@ error:
#ifndef Py_DEFAULT_RECURSION_LIMIT
#define Py_DEFAULT_RECURSION_LIMIT 1000
#endif
static int recursion_limit = Py_DEFAULT_RECURSION_LIMIT;
int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
void
_PyEval_Initialize(struct _ceval_runtime_state *state)
{
state->recursion_limit = Py_DEFAULT_RECURSION_LIMIT;
state->check_recursion_limit = Py_DEFAULT_RECURSION_LIMIT;
_gil_initialize(&state->gil);
}
int
_PyEval_CheckRecursionLimit(void)
{
return _PyRuntime.ceval.check_recursion_limit;
}
int
Py_GetRecursionLimit(void)
{
return recursion_limit;
return _PyRuntime.ceval.recursion_limit;
}
void
Py_SetRecursionLimit(int new_limit)
{
recursion_limit = new_limit;
_Py_CheckRecursionLimit = recursion_limit;
_PyRuntime.ceval.recursion_limit = new_limit;
_PyRuntime.ceval.check_recursion_limit = _PyRuntime.ceval.recursion_limit;
}
/* the macro Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall()
@ -595,6 +585,7 @@ int
_Py_CheckRecursiveCall(const char *where)
{
PyThreadState *tstate = PyThreadState_GET();
int recursion_limit = _PyRuntime.ceval.recursion_limit;
#ifdef USE_STACKCHECK
if (PyOS_CheckStack()) {
@ -603,7 +594,7 @@ _Py_CheckRecursiveCall(const char *where)
return -1;
}
#endif
_Py_CheckRecursionLimit = recursion_limit;
_PyRuntime.ceval.check_recursion_limit = recursion_limit;
if (tstate->recursion_critical)
/* Somebody asked that we don't check for recursion. */
return 0;
@ -642,13 +633,7 @@ static void restore_and_clear_exc_state(PyThreadState *, PyFrameObject *);
static int do_raise(PyObject *, PyObject *);
static int unpack_iterable(PyObject *, int, int, PyObject **);
/* Records whether tracing is on for any thread. Counts the number of
threads for which tstate->c_tracefunc is non-NULL, so if the value
is 0, we know we don't have to check this thread's c_tracefunc.
This speeds up the if statement in PyEval_EvalFrameEx() after
fast_next_opcode*/
static int _Py_TracingPossible = 0;
#define _Py_TracingPossible _PyRuntime.ceval.tracing_possible
PyObject *
@ -779,7 +764,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
#define DISPATCH() \
{ \
if (!_Py_atomic_load_relaxed(&eval_breaker)) { \
if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.eval_breaker)) { \
FAST_DISPATCH(); \
} \
continue; \
@ -827,7 +812,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
/* Code access macros */
/* The integer overflow is checked by an assertion below. */
#define INSTR_OFFSET() (sizeof(_Py_CODEUNIT) * (int)(next_instr - first_instr))
#define INSTR_OFFSET() \
(sizeof(_Py_CODEUNIT) * (int)(next_instr - first_instr))
#define NEXTOPARG() do { \
_Py_CODEUNIT word = *next_instr; \
opcode = _Py_OPCODE(word); \
@ -1080,7 +1066,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
async I/O handler); see Py_AddPendingCall() and
Py_MakePendingCalls() above. */
if (_Py_atomic_load_relaxed(&eval_breaker)) {
if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.eval_breaker)) {
if (_Py_OPCODE(*next_instr) == SETUP_FINALLY ||
_Py_OPCODE(*next_instr) == YIELD_FROM) {
/* Two cases where we skip running signal handlers and other
@ -1097,12 +1083,16 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
*/
goto fast_next_opcode;
}
if (_Py_atomic_load_relaxed(&pendingcalls_to_do)) {
if (_Py_atomic_load_relaxed(
&_PyRuntime.ceval.pending.calls_to_do))
{
if (Py_MakePendingCalls() < 0)
goto error;
}
#ifdef WITH_THREAD
if (_Py_atomic_load_relaxed(&gil_drop_request)) {
if (_Py_atomic_load_relaxed(
&_PyRuntime.ceval.gil_drop_request))
{
/* Give another thread a chance */
if (PyThreadState_Swap(NULL) != tstate)
Py_FatalError("ceval: tstate mix-up");
@ -1113,7 +1103,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
take_gil(tstate);
/* Check if we should make a quick exit. */
if (_Py_Finalizing && _Py_Finalizing != tstate) {
if (_Py_IS_FINALIZING() &&
!_Py_CURRENTLY_FINALIZING(tstate))
{
drop_gil(tstate);
PyThread_exit_thread();
}

View file

@ -8,20 +8,13 @@
/* First some general settings */
/* microseconds (the Python API uses seconds, though) */
#define DEFAULT_INTERVAL 5000
static unsigned long gil_interval = DEFAULT_INTERVAL;
#define INTERVAL (gil_interval >= 1 ? gil_interval : 1)
/* Enable if you want to force the switching of threads at least every `gil_interval` */
#undef FORCE_SWITCHING
#define FORCE_SWITCHING
#define INTERVAL (_PyRuntime.ceval.gil.interval >= 1 ? _PyRuntime.ceval.gil.interval : 1)
/*
Notes about the implementation:
- The GIL is just a boolean variable (gil_locked) whose access is protected
- The GIL is just a boolean variable (locked) whose access is protected
by a mutex (gil_mutex), and whose changes are signalled by a condition
variable (gil_cond). gil_mutex is taken for short periods of time,
and therefore mostly uncontended.
@ -48,7 +41,7 @@ static unsigned long gil_interval = DEFAULT_INTERVAL;
- When a thread releases the GIL and gil_drop_request is set, that thread
ensures that another GIL-awaiting thread gets scheduled.
It does so by waiting on a condition variable (switch_cond) until
the value of gil_last_holder is changed to something else than its
the value of last_holder is changed to something else than its
own thread state pointer, indicating that another thread was able to
take the GIL.
@ -60,11 +53,7 @@ static unsigned long gil_interval = DEFAULT_INTERVAL;
*/
#include "condvar.h"
#ifndef Py_HAVE_CONDVAR
#error You need either a POSIX-compatible or a Windows system!
#endif
#define MUTEX_T PyMUTEX_T
#define MUTEX_INIT(mut) \
if (PyMUTEX_INIT(&(mut))) { \
Py_FatalError("PyMUTEX_INIT(" #mut ") failed"); };
@ -78,7 +67,6 @@ static unsigned long gil_interval = DEFAULT_INTERVAL;
if (PyMUTEX_UNLOCK(&(mut))) { \
Py_FatalError("PyMUTEX_UNLOCK(" #mut ") failed"); };
#define COND_T PyCOND_T
#define COND_INIT(cond) \
if (PyCOND_INIT(&(cond))) { \
Py_FatalError("PyCOND_INIT(" #cond ") failed"); };
@ -103,48 +91,36 @@ static unsigned long gil_interval = DEFAULT_INTERVAL;
} \
#define DEFAULT_INTERVAL 5000
/* Whether the GIL is already taken (-1 if uninitialized). This is atomic
because it can be read without any lock taken in ceval.c. */
static _Py_atomic_int gil_locked = {-1};
/* Number of GIL switches since the beginning. */
static unsigned long gil_switch_number = 0;
/* Last PyThreadState holding / having held the GIL. This helps us know
whether anyone else was scheduled after we dropped the GIL. */
static _Py_atomic_address gil_last_holder = {0};
/* This condition variable allows one or several threads to wait until
the GIL is released. In addition, the mutex also protects the above
variables. */
static COND_T gil_cond;
static MUTEX_T gil_mutex;
#ifdef FORCE_SWITCHING
/* This condition variable helps the GIL-releasing thread wait for
a GIL-awaiting thread to be scheduled and take the GIL. */
static COND_T switch_cond;
static MUTEX_T switch_mutex;
#endif
static void _gil_initialize(struct _gil_runtime_state *state)
{
_Py_atomic_int uninitialized = {-1};
state->locked = uninitialized;
state->interval = DEFAULT_INTERVAL;
}
static int gil_created(void)
{
return _Py_atomic_load_explicit(&gil_locked, _Py_memory_order_acquire) >= 0;
return (_Py_atomic_load_explicit(&_PyRuntime.ceval.gil.locked,
_Py_memory_order_acquire)
) >= 0;
}
static void create_gil(void)
{
MUTEX_INIT(gil_mutex);
MUTEX_INIT(_PyRuntime.ceval.gil.mutex);
#ifdef FORCE_SWITCHING
MUTEX_INIT(switch_mutex);
MUTEX_INIT(_PyRuntime.ceval.gil.switch_mutex);
#endif
COND_INIT(gil_cond);
COND_INIT(_PyRuntime.ceval.gil.cond);
#ifdef FORCE_SWITCHING
COND_INIT(switch_cond);
COND_INIT(_PyRuntime.ceval.gil.switch_cond);
#endif
_Py_atomic_store_relaxed(&gil_last_holder, 0);
_Py_ANNOTATE_RWLOCK_CREATE(&gil_locked);
_Py_atomic_store_explicit(&gil_locked, 0, _Py_memory_order_release);
_Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder, 0);
_Py_ANNOTATE_RWLOCK_CREATE(&_PyRuntime.ceval.gil.locked);
_Py_atomic_store_explicit(&_PyRuntime.ceval.gil.locked, 0,
_Py_memory_order_release);
}
static void destroy_gil(void)
@ -152,54 +128,62 @@ static void destroy_gil(void)
/* some pthread-like implementations tie the mutex to the cond
* and must have the cond destroyed first.
*/
COND_FINI(gil_cond);
MUTEX_FINI(gil_mutex);
COND_FINI(_PyRuntime.ceval.gil.cond);
MUTEX_FINI(_PyRuntime.ceval.gil.mutex);
#ifdef FORCE_SWITCHING
COND_FINI(switch_cond);
MUTEX_FINI(switch_mutex);
COND_FINI(_PyRuntime.ceval.gil.switch_cond);
MUTEX_FINI(_PyRuntime.ceval.gil.switch_mutex);
#endif
_Py_atomic_store_explicit(&gil_locked, -1, _Py_memory_order_release);
_Py_ANNOTATE_RWLOCK_DESTROY(&gil_locked);
_Py_atomic_store_explicit(&_PyRuntime.ceval.gil.locked, -1,
_Py_memory_order_release);
_Py_ANNOTATE_RWLOCK_DESTROY(&_PyRuntime.ceval.gil.locked);
}
static void recreate_gil(void)
{
_Py_ANNOTATE_RWLOCK_DESTROY(&gil_locked);
_Py_ANNOTATE_RWLOCK_DESTROY(&_PyRuntime.ceval.gil.locked);
/* XXX should we destroy the old OS resources here? */
create_gil();
}
static void drop_gil(PyThreadState *tstate)
{
if (!_Py_atomic_load_relaxed(&gil_locked))
if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked))
Py_FatalError("drop_gil: GIL is not locked");
/* tstate is allowed to be NULL (early interpreter init) */
if (tstate != NULL) {
/* Sub-interpreter support: threads might have been switched
under our feet using PyThreadState_Swap(). Fix the GIL last
holder variable so that our heuristics work. */
_Py_atomic_store_relaxed(&gil_last_holder, (uintptr_t)tstate);
_Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder,
(uintptr_t)tstate);
}
MUTEX_LOCK(gil_mutex);
_Py_ANNOTATE_RWLOCK_RELEASED(&gil_locked, /*is_write=*/1);
_Py_atomic_store_relaxed(&gil_locked, 0);
COND_SIGNAL(gil_cond);
MUTEX_UNLOCK(gil_mutex);
MUTEX_LOCK(_PyRuntime.ceval.gil.mutex);
_Py_ANNOTATE_RWLOCK_RELEASED(&_PyRuntime.ceval.gil.locked, /*is_write=*/1);
_Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.locked, 0);
COND_SIGNAL(_PyRuntime.ceval.gil.cond);
MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex);
#ifdef FORCE_SWITCHING
if (_Py_atomic_load_relaxed(&gil_drop_request) && tstate != NULL) {
MUTEX_LOCK(switch_mutex);
if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request) &&
tstate != NULL)
{
MUTEX_LOCK(_PyRuntime.ceval.gil.switch_mutex);
/* Not switched yet => wait */
if ((PyThreadState*)_Py_atomic_load_relaxed(&gil_last_holder) == tstate) {
if (((PyThreadState*)_Py_atomic_load_relaxed(
&_PyRuntime.ceval.gil.last_holder)
) == tstate)
{
RESET_GIL_DROP_REQUEST();
/* NOTE: if COND_WAIT does not atomically start waiting when
releasing the mutex, another thread can run through, take
the GIL and drop it again, and reset the condition
before we even had a chance to wait for it. */
COND_WAIT(switch_cond, switch_mutex);
COND_WAIT(_PyRuntime.ceval.gil.switch_cond,
_PyRuntime.ceval.gil.switch_mutex);
}
MUTEX_UNLOCK(switch_mutex);
MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex);
}
#endif
}
@ -211,60 +195,65 @@ static void take_gil(PyThreadState *tstate)
Py_FatalError("take_gil: NULL tstate");
err = errno;
MUTEX_LOCK(gil_mutex);
MUTEX_LOCK(_PyRuntime.ceval.gil.mutex);
if (!_Py_atomic_load_relaxed(&gil_locked))
if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked))
goto _ready;
while (_Py_atomic_load_relaxed(&gil_locked)) {
while (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked)) {
int timed_out = 0;
unsigned long saved_switchnum;
saved_switchnum = gil_switch_number;
COND_TIMED_WAIT(gil_cond, gil_mutex, INTERVAL, timed_out);
saved_switchnum = _PyRuntime.ceval.gil.switch_number;
COND_TIMED_WAIT(_PyRuntime.ceval.gil.cond, _PyRuntime.ceval.gil.mutex,
INTERVAL, timed_out);
/* If we timed out and no switch occurred in the meantime, it is time
to ask the GIL-holding thread to drop it. */
if (timed_out &&
_Py_atomic_load_relaxed(&gil_locked) &&
gil_switch_number == saved_switchnum) {
_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked) &&
_PyRuntime.ceval.gil.switch_number == saved_switchnum) {
SET_GIL_DROP_REQUEST();
}
}
_ready:
#ifdef FORCE_SWITCHING
/* This mutex must be taken before modifying gil_last_holder (see drop_gil()). */
MUTEX_LOCK(switch_mutex);
/* This mutex must be taken before modifying
_PyRuntime.ceval.gil.last_holder (see drop_gil()). */
MUTEX_LOCK(_PyRuntime.ceval.gil.switch_mutex);
#endif
/* We now hold the GIL */
_Py_atomic_store_relaxed(&gil_locked, 1);
_Py_ANNOTATE_RWLOCK_ACQUIRED(&gil_locked, /*is_write=*/1);
_Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.locked, 1);
_Py_ANNOTATE_RWLOCK_ACQUIRED(&_PyRuntime.ceval.gil.locked, /*is_write=*/1);
if (tstate != (PyThreadState*)_Py_atomic_load_relaxed(&gil_last_holder)) {
_Py_atomic_store_relaxed(&gil_last_holder, (uintptr_t)tstate);
++gil_switch_number;
if (tstate != (PyThreadState*)_Py_atomic_load_relaxed(
&_PyRuntime.ceval.gil.last_holder))
{
_Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder,
(uintptr_t)tstate);
++_PyRuntime.ceval.gil.switch_number;
}
#ifdef FORCE_SWITCHING
COND_SIGNAL(switch_cond);
MUTEX_UNLOCK(switch_mutex);
COND_SIGNAL(_PyRuntime.ceval.gil.switch_cond);
MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex);
#endif
if (_Py_atomic_load_relaxed(&gil_drop_request)) {
if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request)) {
RESET_GIL_DROP_REQUEST();
}
if (tstate->async_exc != NULL) {
_PyEval_SignalAsyncExc();
}
MUTEX_UNLOCK(gil_mutex);
MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex);
errno = err;
}
void _PyEval_SetSwitchInterval(unsigned long microseconds)
{
gil_interval = microseconds;
_PyRuntime.ceval.gil.interval = microseconds;
}
unsigned long _PyEval_GetSwitchInterval()
{
return gil_interval;
return _PyRuntime.ceval.gil.interval;
}

View file

@ -37,27 +37,16 @@
* Condition Variable.
*/
#ifndef _CONDVAR_H_
#define _CONDVAR_H_
#ifndef _CONDVAR_IMPL_H_
#define _CONDVAR_IMPL_H_
#include "Python.h"
#ifndef _POSIX_THREADS
/* This means pthreads are not implemented in libc headers, hence the macro
not present in unistd.h. But they still can be implemented as an external
library (e.g. gnu pth in pthread emulation) */
# ifdef HAVE_PTHREAD_H
# include <pthread.h> /* _POSIX_THREADS */
# endif
#endif
#include "internal/_condvar.h"
#ifdef _POSIX_THREADS
/*
* POSIX support
*/
#define Py_HAVE_CONDVAR
#include <pthread.h>
#define PyCOND_ADD_MICROSECONDS(tv, interval) \
do { /* TODO: add overflow and truncation checks */ \
@ -74,13 +63,11 @@ do { /* TODO: add overflow and truncation checks */ \
#endif
/* The following functions return 0 on success, nonzero on error */
#define PyMUTEX_T pthread_mutex_t
#define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL)
#define PyMUTEX_FINI(mut) pthread_mutex_destroy(mut)
#define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut)
#define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut)
#define PyCOND_T pthread_cond_t
#define PyCOND_INIT(cond) pthread_cond_init((cond), NULL)
#define PyCOND_FINI(cond) pthread_cond_destroy(cond)
#define PyCOND_SIGNAL(cond) pthread_cond_signal(cond)
@ -116,45 +103,11 @@ PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us)
* Emulated condition variables ones that work with XP and later, plus
* example native support on VISTA and onwards.
*/
#define Py_HAVE_CONDVAR
/* include windows if it hasn't been done before */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/* options */
/* non-emulated condition variables are provided for those that want
* to target Windows Vista. Modify this macro to enable them.
*/
#ifndef _PY_EMULATED_WIN_CV
#define _PY_EMULATED_WIN_CV 1 /* use emulated condition variables */
#endif
/* fall back to emulation if not targeting Vista */
#if !defined NTDDI_VISTA || NTDDI_VERSION < NTDDI_VISTA
#undef _PY_EMULATED_WIN_CV
#define _PY_EMULATED_WIN_CV 1
#endif
#if _PY_EMULATED_WIN_CV
/* The mutex is a CriticalSection object and
The condition variables is emulated with the help of a semaphore.
Semaphores are available on Windows XP (2003 server) and later.
We use a Semaphore rather than an auto-reset event, because although
an auto-resent event might appear to solve the lost-wakeup bug (race
condition between releasing the outer lock and waiting) because it
maintains state even though a wait hasn't happened, there is still
a lost wakeup problem if more than one thread are interrupted in the
critical place. A semaphore solves that, because its state is counted,
not Boolean.
Because it is ok to signal a condition variable with no one
waiting, we need to keep track of the number of
waiting threads. Otherwise, the semaphore's state could rise
without bound. This also helps reduce the number of "spurious wakeups"
that would otherwise happen.
This implementation still has the problem that the threads woken
with a "signal" aren't necessarily those that are already
@ -168,8 +121,6 @@ PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us)
http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
*/
typedef CRITICAL_SECTION PyMUTEX_T;
Py_LOCAL_INLINE(int)
PyMUTEX_INIT(PyMUTEX_T *cs)
{
@ -198,15 +149,6 @@ PyMUTEX_UNLOCK(PyMUTEX_T *cs)
return 0;
}
/* The ConditionVariable object. From XP onwards it is easily emulated with
* a Semaphore
*/
typedef struct _PyCOND_T
{
HANDLE sem;
int waiting; /* to allow PyCOND_SIGNAL to be a no-op */
} PyCOND_T;
Py_LOCAL_INLINE(int)
PyCOND_INIT(PyCOND_T *cv)
@ -304,12 +246,7 @@ PyCOND_BROADCAST(PyCOND_T *cv)
return 0;
}
#else
/* Use native Win7 primitives if build target is Win7 or higher */
/* SRWLOCK is faster and better than CriticalSection */
typedef SRWLOCK PyMUTEX_T;
#else /* !_PY_EMULATED_WIN_CV */
Py_LOCAL_INLINE(int)
PyMUTEX_INIT(PyMUTEX_T *cs)
@ -339,8 +276,6 @@ PyMUTEX_UNLOCK(PyMUTEX_T *cs)
}
typedef CONDITION_VARIABLE PyCOND_T;
Py_LOCAL_INLINE(int)
PyCOND_INIT(PyCOND_T *cv)
{
@ -387,4 +322,4 @@ PyCOND_BROADCAST(PyCOND_T *cv)
#endif /* _POSIX_THREADS, NT_THREADS */
#endif /* _CONDVAR_H_ */
#endif /* _CONDVAR_IMPL_H_ */

View file

@ -77,6 +77,30 @@ extern void _PyGILState_Init(PyInterpreterState *, PyThreadState *);
extern void _PyGILState_Fini(void);
#endif /* WITH_THREAD */
_PyRuntimeState _PyRuntime = {};
void
_PyRuntime_Initialize(void)
{
/* XXX We only initialize once in the process, which aligns with
the static initialization of the former globals now found in
_PyRuntime. However, _PyRuntime *should* be initialized with
every Py_Initialize() call, but doing so breaks the runtime.
This is because the runtime state is not properly finalized
currently. */
static int initialized = 0;
if (initialized)
return;
initialized = 1;
_PyRuntimeState_Init(&_PyRuntime);
}
void
_PyRuntime_Finalize(void)
{
_PyRuntimeState_Fini(&_PyRuntime);
}
/* Global configuration variable declarations are in pydebug.h */
/* XXX (ncoghlan): move those declarations to pylifecycle.h? */
int Py_DebugFlag; /* Needed by parser.c */
@ -100,8 +124,6 @@ int Py_LegacyWindowsFSEncodingFlag = 0; /* Uses mbcs instead of utf-8 */
int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */
#endif
PyThreadState *_Py_Finalizing = NULL;
/* Hack to force loading of object files */
int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \
PyOS_mystrnicmp; /* Python/pystrcmp.o */
@ -119,19 +141,17 @@ PyModule_GetWarningsModule(void)
*
* Can be called prior to Py_Initialize.
*/
int _Py_CoreInitialized = 0;
int _Py_Initialized = 0;
int
_Py_IsCoreInitialized(void)
{
return _Py_CoreInitialized;
return _PyRuntime.core_initialized;
}
int
Py_IsInitialized(void)
{
return _Py_Initialized;
return _PyRuntime.initialized;
}
/* Helper to allow an embedding application to override the normal
@ -544,14 +564,16 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
_PyCoreConfig core_config = _PyCoreConfig_INIT;
_PyMainInterpreterConfig preinit_config = _PyMainInterpreterConfig_INIT;
_PyRuntime_Initialize();
if (config != NULL) {
core_config = *config;
}
if (_Py_Initialized) {
if (_PyRuntime.initialized) {
Py_FatalError("Py_InitializeCore: main interpreter already initialized");
}
if (_Py_CoreInitialized) {
if (_PyRuntime.core_initialized) {
Py_FatalError("Py_InitializeCore: runtime core already initialized");
}
@ -564,7 +586,14 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
* threads still hanging around from a previous Py_Initialize/Finalize
* pair :(
*/
_Py_Finalizing = NULL;
_PyRuntime.finalizing = NULL;
if (_PyMem_SetupAllocators(core_config.allocator) < 0) {
fprintf(stderr,
"Error in PYTHONMALLOC: unknown allocator \"%s\"!\n",
core_config.allocator);
exit(1);
}
#ifdef __ANDROID__
/* Passing "" to setlocale() on Android requests the C locale rather
@ -606,7 +635,7 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
Py_HashRandomizationFlag = 1;
}
_PyInterpreterState_Init();
_PyInterpreterState_Enable(&_PyRuntime);
interp = PyInterpreterState_New();
if (interp == NULL)
Py_FatalError("Py_InitializeCore: can't make main interpreter");
@ -698,7 +727,7 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
}
/* Only when we get here is the runtime core fully initialized */
_Py_CoreInitialized = 1;
_PyRuntime.core_initialized = 1;
}
/* Read configuration settings from standard locations
@ -739,10 +768,10 @@ int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
PyInterpreterState *interp;
PyThreadState *tstate;
if (!_Py_CoreInitialized) {
if (!_PyRuntime.core_initialized) {
Py_FatalError("Py_InitializeMainInterpreter: runtime core not initialized");
}
if (_Py_Initialized) {
if (_PyRuntime.initialized) {
Py_FatalError("Py_InitializeMainInterpreter: main interpreter already initialized");
}
@ -763,7 +792,7 @@ int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
* This means anything which needs support from extension modules
* or pure Python code in the standard library won't work.
*/
_Py_Initialized = 1;
_PyRuntime.initialized = 1;
return 0;
}
/* TODO: Report exceptions rather than fatal errors below here */
@ -808,7 +837,7 @@ int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
Py_XDECREF(warnings_module);
}
_Py_Initialized = 1;
_PyRuntime.initialized = 1;
if (!Py_NoSiteFlag)
initsite(); /* Module site */
@ -924,7 +953,7 @@ Py_FinalizeEx(void)
PyThreadState *tstate;
int status = 0;
if (!_Py_Initialized)
if (!_PyRuntime.initialized)
return status;
wait_for_thread_shutdown();
@ -946,9 +975,9 @@ Py_FinalizeEx(void)
/* Remaining threads (e.g. daemon threads) will automatically exit
after taking the GIL (in PyEval_RestoreThread()). */
_Py_Finalizing = tstate;
_Py_Initialized = 0;
_Py_CoreInitialized = 0;
_PyRuntime.finalizing = tstate;
_PyRuntime.initialized = 0;
_PyRuntime.core_initialized = 0;
/* Flush sys.stdout and sys.stderr */
if (flush_std_files() < 0) {
@ -1110,6 +1139,7 @@ Py_FinalizeEx(void)
#endif
call_ll_exitfuncs();
_PyRuntime_Finalize();
return status;
}
@ -1139,7 +1169,7 @@ Py_NewInterpreter(void)
PyThreadState *tstate, *save_tstate;
PyObject *bimod, *sysmod;
if (!_Py_Initialized)
if (!_PyRuntime.initialized)
Py_FatalError("Py_NewInterpreter: call Py_Initialize first");
#ifdef WITH_THREAD
@ -1854,20 +1884,19 @@ exit:
# include "pythread.h"
#endif
static void (*pyexitfunc)(void) = NULL;
/* For the atexit module. */
void _Py_PyAtExit(void (*func)(void))
{
pyexitfunc = func;
_PyRuntime.pyexitfunc = func;
}
static void
call_py_exitfuncs(void)
{
if (pyexitfunc == NULL)
if (_PyRuntime.pyexitfunc == NULL)
return;
(*pyexitfunc)();
(*_PyRuntime.pyexitfunc)();
PyErr_Clear();
}
@ -1900,22 +1929,19 @@ wait_for_thread_shutdown(void)
}
#define NEXITFUNCS 32
static void (*exitfuncs[NEXITFUNCS])(void);
static int nexitfuncs = 0;
int Py_AtExit(void (*func)(void))
{
if (nexitfuncs >= NEXITFUNCS)
if (_PyRuntime.nexitfuncs >= NEXITFUNCS)
return -1;
exitfuncs[nexitfuncs++] = func;
_PyRuntime.exitfuncs[_PyRuntime.nexitfuncs++] = func;
return 0;
}
static void
call_ll_exitfuncs(void)
{
while (nexitfuncs > 0)
(*exitfuncs[--nexitfuncs])();
while (_PyRuntime.nexitfuncs > 0)
(*_PyRuntime.exitfuncs[--_PyRuntime.nexitfuncs])();
fflush(stdout);
fflush(stderr);

View file

@ -34,55 +34,66 @@ to avoid the expense of doing their own locking).
extern "C" {
#endif
int _PyGILState_check_enabled = 1;
void
_PyRuntimeState_Init(_PyRuntimeState *runtime)
{
_PyRuntimeState initial = {};
*runtime = initial;
_PyObject_Initialize(&runtime->obj);
_PyMem_Initialize(&runtime->mem);
_PyGC_Initialize(&runtime->gc);
_PyEval_Initialize(&runtime->ceval);
runtime->gilstate.check_enabled = 1;
runtime->gilstate.autoTLSkey = -1;
#ifdef WITH_THREAD
#include "pythread.h"
static PyThread_type_lock head_mutex = NULL; /* Protects interp->tstate_head */
#define HEAD_INIT() (void)(head_mutex || (head_mutex = PyThread_allocate_lock()))
#define HEAD_LOCK() PyThread_acquire_lock(head_mutex, WAIT_LOCK)
#define HEAD_UNLOCK() PyThread_release_lock(head_mutex)
runtime->interpreters.mutex = PyThread_allocate_lock();
if (runtime->interpreters.mutex == NULL)
Py_FatalError("Can't initialize threads for interpreter");
#endif
runtime->interpreters.next_id = -1;
}
/* The single PyInterpreterState used by this process'
GILState implementation
*/
/* TODO: Given interp_main, it may be possible to kill this ref */
static PyInterpreterState *autoInterpreterState = NULL;
static int autoTLSkey = -1;
void
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
{
#ifdef WITH_THREAD
if (runtime->interpreters.mutex != NULL) {
PyThread_free_lock(runtime->interpreters.mutex);
runtime->interpreters.mutex = NULL;
}
#endif
}
#ifdef WITH_THREAD
#define HEAD_LOCK() PyThread_acquire_lock(_PyRuntime.interpreters.mutex, \
WAIT_LOCK)
#define HEAD_UNLOCK() PyThread_release_lock(_PyRuntime.interpreters.mutex)
#else
#define HEAD_INIT() /* Nothing */
#define HEAD_LOCK() /* Nothing */
#define HEAD_UNLOCK() /* Nothing */
#endif
static PyInterpreterState *interp_head = NULL;
static PyInterpreterState *interp_main = NULL;
/* Assuming the current thread holds the GIL, this is the
PyThreadState for the current thread. */
_Py_atomic_address _PyThreadState_Current = {0};
PyThreadFrameGetter _PyThreadState_GetFrame = NULL;
#ifdef WITH_THREAD
static void _PyGILState_NoteThreadState(PyThreadState* tstate);
#endif
/* _next_interp_id is an auto-numbered sequence of small integers.
It gets initialized in _PyInterpreterState_Init(), which is called
in Py_Initialize(), and used in PyInterpreterState_New(). A negative
interpreter ID indicates an error occurred. The main interpreter
will always have an ID of 0. Overflow results in a RuntimeError.
If that becomes a problem later then we can adjust, e.g. by using
a Python int.
We initialize this to -1 so that the pre-Py_Initialize() value
results in an error. */
static int64_t _next_interp_id = -1;
void
_PyInterpreterState_Init(void)
_PyInterpreterState_Enable(_PyRuntimeState *runtime)
{
_next_interp_id = 0;
runtime->interpreters.next_id = 0;
#ifdef WITH_THREAD
/* Since we only call _PyRuntimeState_Init() once per process
(see _PyRuntime_Initialize()), we make sure the mutex is
initialized here. */
if (runtime->interpreters.mutex == NULL) {
runtime->interpreters.mutex = PyThread_allocate_lock();
if (runtime->interpreters.mutex == NULL)
Py_FatalError("Can't initialize threads for interpreter");
}
#endif
}
PyInterpreterState *
@ -92,16 +103,16 @@ PyInterpreterState_New(void)
PyMem_RawMalloc(sizeof(PyInterpreterState));
if (interp != NULL) {
HEAD_INIT();
#ifdef WITH_THREAD
if (head_mutex == NULL)
Py_FatalError("Can't initialize threads for interpreter");
#endif
interp->modules_by_index = NULL;
interp->sysdict = NULL;
interp->builtins = NULL;
interp->builtins_copy = NULL;
interp->tstate_head = NULL;
interp->check_interval = 100;
interp->warnoptions = NULL;
interp->xoptions = NULL;
interp->num_threads = 0;
interp->pythread_stacksize = 0;
interp->codec_search_path = NULL;
interp->codec_search_cache = NULL;
interp->codec_error_registry = NULL;
@ -125,19 +136,19 @@ PyInterpreterState_New(void)
#endif
HEAD_LOCK();
interp->next = interp_head;
if (interp_main == NULL) {
interp_main = interp;
interp->next = _PyRuntime.interpreters.head;
if (_PyRuntime.interpreters.main == NULL) {
_PyRuntime.interpreters.main = interp;
}
interp_head = interp;
if (_next_interp_id < 0) {
_PyRuntime.interpreters.head = interp;
if (_PyRuntime.interpreters.next_id < 0) {
/* overflow or Py_Initialize() not called! */
PyErr_SetString(PyExc_RuntimeError,
"failed to get an interpreter ID");
interp = NULL;
} else {
interp->id = _next_interp_id;
_next_interp_id += 1;
interp->id = _PyRuntime.interpreters.next_id;
_PyRuntime.interpreters.next_id += 1;
}
HEAD_UNLOCK();
}
@ -189,7 +200,7 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
PyInterpreterState **p;
zapthreads(interp);
HEAD_LOCK();
for (p = &interp_head; ; p = &(*p)->next) {
for (p = &_PyRuntime.interpreters.head; ; p = &(*p)->next) {
if (*p == NULL)
Py_FatalError(
"PyInterpreterState_Delete: invalid interp");
@ -199,19 +210,13 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
if (interp->tstate_head != NULL)
Py_FatalError("PyInterpreterState_Delete: remaining threads");
*p = interp->next;
if (interp_main == interp) {
interp_main = NULL;
if (interp_head != NULL)
if (_PyRuntime.interpreters.main == interp) {
_PyRuntime.interpreters.main = NULL;
if (_PyRuntime.interpreters.head != NULL)
Py_FatalError("PyInterpreterState_Delete: remaining subinterpreters");
}
HEAD_UNLOCK();
PyMem_RawFree(interp);
#ifdef WITH_THREAD
if (interp_head == NULL && head_mutex != NULL) {
PyThread_free_lock(head_mutex);
head_mutex = NULL;
}
#endif
}
@ -499,8 +504,11 @@ PyThreadState_Delete(PyThreadState *tstate)
if (tstate == GET_TSTATE())
Py_FatalError("PyThreadState_Delete: tstate is still current");
#ifdef WITH_THREAD
if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate)
PyThread_delete_key_value(autoTLSkey);
if (_PyRuntime.gilstate.autoInterpreterState &&
PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == tstate)
{
PyThread_delete_key_value(_PyRuntime.gilstate.autoTLSkey);
}
#endif /* WITH_THREAD */
tstate_delete_common(tstate);
}
@ -515,8 +523,11 @@ PyThreadState_DeleteCurrent()
Py_FatalError(
"PyThreadState_DeleteCurrent: no current tstate");
tstate_delete_common(tstate);
if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate)
PyThread_delete_key_value(autoTLSkey);
if (_PyRuntime.gilstate.autoInterpreterState &&
PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == tstate)
{
PyThread_delete_key_value(_PyRuntime.gilstate.autoTLSkey);
}
SET_TSTATE(NULL);
PyEval_ReleaseLock();
}
@ -676,13 +687,13 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
PyInterpreterState *
PyInterpreterState_Head(void)
{
return interp_head;
return _PyRuntime.interpreters.head;
}
PyInterpreterState *
PyInterpreterState_Main(void)
{
return interp_main;
return _PyRuntime.interpreters.main;
}
PyInterpreterState *
@ -722,7 +733,7 @@ _PyThread_CurrentFrames(void)
* need to grab head_mutex for the duration.
*/
HEAD_LOCK();
for (i = interp_head; i != NULL; i = i->next) {
for (i = _PyRuntime.interpreters.head; i != NULL; i = i->next) {
PyThreadState *t;
for (t = i->tstate_head; t != NULL; t = t->next) {
PyObject *id;
@ -774,11 +785,11 @@ void
_PyGILState_Init(PyInterpreterState *i, PyThreadState *t)
{
assert(i && t); /* must init with valid states */
autoTLSkey = PyThread_create_key();
if (autoTLSkey == -1)
_PyRuntime.gilstate.autoTLSkey = PyThread_create_key();
if (_PyRuntime.gilstate.autoTLSkey == -1)
Py_FatalError("Could not allocate TLS entry");
autoInterpreterState = i;
assert(PyThread_get_key_value(autoTLSkey) == NULL);
_PyRuntime.gilstate.autoInterpreterState = i;
assert(PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == NULL);
assert(t->gilstate_counter == 0);
_PyGILState_NoteThreadState(t);
@ -787,15 +798,15 @@ _PyGILState_Init(PyInterpreterState *i, PyThreadState *t)
PyInterpreterState *
_PyGILState_GetInterpreterStateUnsafe(void)
{
return autoInterpreterState;
return _PyRuntime.gilstate.autoInterpreterState;
}
void
_PyGILState_Fini(void)
{
PyThread_delete_key(autoTLSkey);
autoTLSkey = -1;
autoInterpreterState = NULL;
PyThread_delete_key(_PyRuntime.gilstate.autoTLSkey);
_PyRuntime.gilstate.autoTLSkey = -1;
_PyRuntime.gilstate.autoInterpreterState = NULL;
}
/* Reset the TLS key - called by PyOS_AfterFork_Child().
@ -806,17 +817,19 @@ void
_PyGILState_Reinit(void)
{
#ifdef WITH_THREAD
head_mutex = NULL;
HEAD_INIT();
_PyRuntime.interpreters.mutex = PyThread_allocate_lock();
if (_PyRuntime.interpreters.mutex == NULL)
Py_FatalError("Can't initialize threads for interpreter");
#endif
PyThreadState *tstate = PyGILState_GetThisThreadState();
PyThread_delete_key(autoTLSkey);
if ((autoTLSkey = PyThread_create_key()) == -1)
PyThread_delete_key(_PyRuntime.gilstate.autoTLSkey);
if ((_PyRuntime.gilstate.autoTLSkey = PyThread_create_key()) == -1)
Py_FatalError("Could not allocate TLS entry");
/* If the thread had an associated auto thread state, reassociate it with
* the new key. */
if (tstate && PyThread_set_key_value(autoTLSkey, (void *)tstate) < 0)
if (tstate && PyThread_set_key_value(_PyRuntime.gilstate.autoTLSkey,
(void *)tstate) < 0)
Py_FatalError("Couldn't create autoTLSkey mapping");
}
@ -831,7 +844,7 @@ _PyGILState_NoteThreadState(PyThreadState* tstate)
/* If autoTLSkey isn't initialized, this must be the very first
threadstate created in Py_Initialize(). Don't do anything for now
(we'll be back here when _PyGILState_Init is called). */
if (!autoInterpreterState)
if (!_PyRuntime.gilstate.autoInterpreterState)
return;
/* Stick the thread state for this thread in thread local storage.
@ -846,9 +859,13 @@ _PyGILState_NoteThreadState(PyThreadState* tstate)
The first thread state created for that given OS level thread will
"win", which seems reasonable behaviour.
*/
if (PyThread_get_key_value(autoTLSkey) == NULL) {
if (PyThread_set_key_value(autoTLSkey, (void *)tstate) < 0)
if (PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == NULL) {
if ((PyThread_set_key_value(_PyRuntime.gilstate.autoTLSkey,
(void *)tstate)
) < 0)
{
Py_FatalError("Couldn't create autoTLSkey mapping");
}
}
/* PyGILState_Release must not try to delete this thread state. */
@ -859,9 +876,10 @@ _PyGILState_NoteThreadState(PyThreadState* tstate)
PyThreadState *
PyGILState_GetThisThreadState(void)
{
if (autoInterpreterState == NULL)
if (_PyRuntime.gilstate.autoInterpreterState == NULL)
return NULL;
return (PyThreadState *)PyThread_get_key_value(autoTLSkey);
return (PyThreadState *)PyThread_get_key_value(
_PyRuntime.gilstate.autoTLSkey);
}
int
@ -872,7 +890,7 @@ PyGILState_Check(void)
if (!_PyGILState_check_enabled)
return 1;
if (autoTLSkey == -1)
if (_PyRuntime.gilstate.autoTLSkey == -1)
return 1;
tstate = GET_TSTATE();
@ -892,8 +910,10 @@ PyGILState_Ensure(void)
spells out other issues. Embedders are expected to have
called Py_Initialize() and usually PyEval_InitThreads().
*/
assert(autoInterpreterState); /* Py_Initialize() hasn't been called! */
tcur = (PyThreadState *)PyThread_get_key_value(autoTLSkey);
/* Py_Initialize() hasn't been called! */
assert(_PyRuntime.gilstate.autoInterpreterState);
tcur = (PyThreadState *)PyThread_get_key_value(
_PyRuntime.gilstate.autoTLSkey);
if (tcur == NULL) {
/* At startup, Python has no concrete GIL. If PyGILState_Ensure() is
called from a new thread for the first time, we need the create the
@ -901,7 +921,7 @@ PyGILState_Ensure(void)
PyEval_InitThreads();
/* Create a new thread state for this thread */
tcur = PyThreadState_New(autoInterpreterState);
tcur = PyThreadState_New(_PyRuntime.gilstate.autoInterpreterState);
if (tcur == NULL)
Py_FatalError("Couldn't create thread-state for new thread");
/* This is our thread state! We'll need to delete it in the
@ -926,7 +946,7 @@ void
PyGILState_Release(PyGILState_STATE oldstate)
{
PyThreadState *tcur = (PyThreadState *)PyThread_get_key_value(
autoTLSkey);
_PyRuntime.gilstate.autoTLSkey);
if (tcur == NULL)
Py_FatalError("auto-releasing thread-state, "
"but no thread-state for this thread");

View file

@ -519,8 +519,6 @@ Return the profiling function set with sys.setprofile.\n\
See the profiler chapter in the library manual."
);
static int _check_interval = 100;
static PyObject *
sys_setcheckinterval(PyObject *self, PyObject *args)
{
@ -529,7 +527,8 @@ sys_setcheckinterval(PyObject *self, PyObject *args)
"are deprecated. Use sys.setswitchinterval() "
"instead.", 1) < 0)
return NULL;
if (!PyArg_ParseTuple(args, "i:setcheckinterval", &_check_interval))
PyInterpreterState *interp = PyThreadState_GET()->interp;
if (!PyArg_ParseTuple(args, "i:setcheckinterval", &interp->check_interval))
return NULL;
Py_RETURN_NONE;
}
@ -549,7 +548,8 @@ sys_getcheckinterval(PyObject *self, PyObject *args)
"are deprecated. Use sys.getswitchinterval() "
"instead.", 1) < 0)
return NULL;
return PyLong_FromLong(_check_interval);
PyInterpreterState *interp = PyThreadState_GET()->interp;
return PyLong_FromLong(interp->check_interval);
}
PyDoc_STRVAR(getcheckinterval_doc,
@ -1339,7 +1339,7 @@ Clear the internal type lookup cache.");
static PyObject *
sys_is_finalizing(PyObject* self, PyObject* args)
{
return PyBool_FromLong(_Py_Finalizing != NULL);
return PyBool_FromLong(_Py_IS_FINALIZING());
}
PyDoc_STRVAR(is_finalizing_doc,
@ -1479,11 +1479,24 @@ list_builtin_module_names(void)
return list;
}
static PyObject *warnoptions = NULL;
static PyObject *
get_warnoptions(void)
{
PyObject *warnoptions = PyThreadState_GET()->interp->warnoptions;
if (warnoptions == NULL || !PyList_Check(warnoptions)) {
Py_XDECREF(warnoptions);
warnoptions = PyList_New(0);
if (warnoptions == NULL)
return NULL;
PyThreadState_GET()->interp->warnoptions = warnoptions;
}
return warnoptions;
}
void
PySys_ResetWarnOptions(void)
{
PyObject *warnoptions = PyThreadState_GET()->interp->warnoptions;
if (warnoptions == NULL || !PyList_Check(warnoptions))
return;
PyList_SetSlice(warnoptions, 0, PyList_GET_SIZE(warnoptions), NULL);
@ -1492,12 +1505,9 @@ PySys_ResetWarnOptions(void)
void
PySys_AddWarnOptionUnicode(PyObject *unicode)
{
if (warnoptions == NULL || !PyList_Check(warnoptions)) {
Py_XDECREF(warnoptions);
warnoptions = PyList_New(0);
if (warnoptions == NULL)
return;
}
PyObject *warnoptions = get_warnoptions();
if (warnoptions == NULL)
return;
PyList_Append(warnoptions, unicode);
}
@ -1515,17 +1525,20 @@ PySys_AddWarnOption(const wchar_t *s)
int
PySys_HasWarnOptions(void)
{
PyObject *warnoptions = PyThreadState_GET()->interp->warnoptions;
return (warnoptions != NULL && (PyList_Size(warnoptions) > 0)) ? 1 : 0;
}
static PyObject *xoptions = NULL;
static PyObject *
get_xoptions(void)
{
PyObject *xoptions = PyThreadState_GET()->interp->xoptions;
if (xoptions == NULL || !PyDict_Check(xoptions)) {
Py_XDECREF(xoptions);
xoptions = PyDict_New();
if (xoptions == NULL)
return NULL;
PyThreadState_GET()->interp->xoptions = xoptions;
}
return xoptions;
}
@ -2130,17 +2143,15 @@ _PySys_EndInit(PyObject *sysdict)
SET_SYS_FROM_STRING_INT_RESULT("base_exec_prefix",
PyUnicode_FromWideChar(Py_GetExecPrefix(), -1));
if (warnoptions == NULL) {
warnoptions = PyList_New(0);
if (warnoptions == NULL)
return -1;
}
PyObject *warnoptions = get_warnoptions();
if (warnoptions == NULL)
return -1;
SET_SYS_FROM_STRING_BORROW_INT_RESULT("warnoptions", warnoptions);
SET_SYS_FROM_STRING_INT_RESULT("warnoptions",
PyList_GetSlice(warnoptions,
0, Py_SIZE(warnoptions)));
SET_SYS_FROM_STRING_BORROW_INT_RESULT("_xoptions", get_xoptions());
PyObject *xoptions = get_xoptions();
if (xoptions == NULL)
return -1;
SET_SYS_FROM_STRING_BORROW_INT_RESULT("_xoptions", xoptions);
if (PyErr_Occurred())
return -1;

View file

@ -76,11 +76,6 @@ PyThread_init_thread(void)
PyThread__init_thread();
}
/* Support for runtime thread stack size tuning.
A value of 0 means using the platform's default stack size
or the size specified by the THREAD_STACK_SIZE macro. */
static size_t _pythread_stacksize = 0;
#if defined(_POSIX_THREADS)
# define PYTHREAD_NAME "pthread"
# include "thread_pthread.h"
@ -96,7 +91,7 @@ static size_t _pythread_stacksize = 0;
size_t
PyThread_get_stacksize(void)
{
return _pythread_stacksize;
return PyThreadState_GET()->interp->pythread_stacksize;
}
/* Only platforms defining a THREAD_SET_STACKSIZE() macro

View file

@ -189,9 +189,10 @@ PyThread_start_new_thread(void (*func)(void *), void *arg)
return PYTHREAD_INVALID_THREAD_ID;
obj->func = func;
obj->arg = arg;
PyThreadState *tstate = PyThreadState_GET();
size_t stacksize = tstate ? tstate->interp->pythread_stacksize : 0;
hThread = (HANDLE)_beginthreadex(0,
Py_SAFE_DOWNCAST(_pythread_stacksize,
Py_ssize_t, unsigned int),
Py_SAFE_DOWNCAST(stacksize, Py_ssize_t, unsigned int),
bootstrap, obj,
0, &threadID);
if (hThread == 0) {
@ -332,13 +333,13 @@ _pythread_nt_set_stacksize(size_t size)
{
/* set to default */
if (size == 0) {
_pythread_stacksize = 0;
PyThreadState_GET()->interp->pythread_stacksize = 0;
return 0;
}
/* valid range? */
if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) {
_pythread_stacksize = size;
PyThreadState_GET()->interp->pythread_stacksize = size;
return 0;
}

View file

@ -205,8 +205,9 @@ PyThread_start_new_thread(void (*func)(void *), void *arg)
return PYTHREAD_INVALID_THREAD_ID;
#endif
#if defined(THREAD_STACK_SIZE)
tss = (_pythread_stacksize != 0) ? _pythread_stacksize
: THREAD_STACK_SIZE;
PyThreadState *tstate = PyThreadState_GET();
size_t stacksize = tstate ? tstate->interp->pythread_stacksize : 0;
tss = (stacksize != 0) ? stacksize : THREAD_STACK_SIZE;
if (tss != 0) {
if (pthread_attr_setstacksize(&attrs, tss) != 0) {
pthread_attr_destroy(&attrs);
@ -578,7 +579,7 @@ _pythread_pthread_set_stacksize(size_t size)
/* set to default */
if (size == 0) {
_pythread_stacksize = 0;
PyThreadState_GET()->interp->pythread_stacksize = 0;
return 0;
}
@ -595,7 +596,7 @@ _pythread_pthread_set_stacksize(size_t size)
rc = pthread_attr_setstacksize(&attrs, size);
pthread_attr_destroy(&attrs);
if (rc == 0) {
_pythread_stacksize = size;
PyThreadState_GET()->interp->pythread_stacksize = size;
return 0;
}
}