Skip to content

Commit 76d5abc

Browse files
bpo-30860: Consolidate stateful runtime globals. (python#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).
1 parent 501b324 commit 76d5abc

40 files changed

+2731
-1331
lines changed

Include/Python.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,8 @@
133133
#include "fileutils.h"
134134
#include "pyfpe.h"
135135

136+
#ifdef Py_BUILD_CORE
137+
#include "internal/_Python.h"
138+
#endif
139+
136140
#endif /* !Py_PYTHON_H */

Include/ceval.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,12 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void);
9393
PyThreadState_GET()->overflowed = 0; \
9494
} while(0)
9595
PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
96-
PyAPI_DATA(int) _Py_CheckRecursionLimit;
96+
#ifdef Py_BUILD_CORE
97+
#define _Py_CheckRecursionLimit _PyRuntime.ceval.check_recursion_limit
98+
#else
99+
PyAPI_FUNC(int) _PyEval_CheckRecursionLimit(void);
100+
#define _Py_CheckRecursionLimit _PyEval_CheckRecursionLimit()
101+
#endif
97102

98103
#ifdef USE_STACKCHECK
99104
/* With USE_STACKCHECK, we artificially decrement the recursion limit in order

Include/internal/_Python.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef _Py_PYTHON_H
2+
#define _Py_PYTHON_H
3+
/* Since this is a "meta-include" file, no #ifdef __cplusplus / extern "C" { */
4+
5+
/* Include all internal Python header files */
6+
7+
#ifndef Py_BUILD_CORE
8+
#error "Internal headers are not available externally."
9+
#endif
10+
11+
#include "_mem.h"
12+
#include "_ceval.h"
13+
#include "_warnings.h"
14+
#include "_pystate.h"
15+
16+
#endif /* !_Py_PYTHON_H */

Include/internal/_ceval.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#ifndef _Py_CEVAL_H
2+
#define _Py_CEVAL_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#include "ceval.h"
8+
#include "compile.h"
9+
#include "pyatomic.h"
10+
11+
#ifdef WITH_THREAD
12+
#include "pythread.h"
13+
#endif
14+
15+
struct _pending_calls {
16+
unsigned long main_thread;
17+
#ifdef WITH_THREAD
18+
PyThread_type_lock lock;
19+
/* Request for running pending calls. */
20+
_Py_atomic_int calls_to_do;
21+
/* Request for looking at the `async_exc` field of the current
22+
thread state.
23+
Guarded by the GIL. */
24+
int async_exc;
25+
#define NPENDINGCALLS 32
26+
struct {
27+
int (*func)(void *);
28+
void *arg;
29+
} calls[NPENDINGCALLS];
30+
int first;
31+
int last;
32+
#else /* ! WITH_THREAD */
33+
_Py_atomic_int calls_to_do;
34+
#define NPENDINGCALLS 32
35+
struct {
36+
int (*func)(void *);
37+
void *arg;
38+
} calls[NPENDINGCALLS];
39+
volatile int first;
40+
volatile int last;
41+
#endif /* WITH_THREAD */
42+
};
43+
44+
#include "_gil.h"
45+
46+
struct _ceval_runtime_state {
47+
int recursion_limit;
48+
int check_recursion_limit;
49+
/* Records whether tracing is on for any thread. Counts the number
50+
of threads for which tstate->c_tracefunc is non-NULL, so if the
51+
value is 0, we know we don't have to check this thread's
52+
c_tracefunc. This speeds up the if statement in
53+
PyEval_EvalFrameEx() after fast_next_opcode. */
54+
int tracing_possible;
55+
/* This single variable consolidates all requests to break out of
56+
the fast path in the eval loop. */
57+
_Py_atomic_int eval_breaker;
58+
#ifdef WITH_THREAD
59+
/* Request for dropping the GIL */
60+
_Py_atomic_int gil_drop_request;
61+
#endif
62+
struct _pending_calls pending;
63+
struct _gil_runtime_state gil;
64+
};
65+
66+
PyAPI_FUNC(void) _PyEval_Initialize(struct _ceval_runtime_state *);
67+
68+
#ifdef __cplusplus
69+
}
70+
#endif
71+
#endif /* !_Py_CEVAL_H */

Include/internal/_condvar.h

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#ifndef _CONDVAR_H_
2+
#define _CONDVAR_H_
3+
4+
#ifndef _POSIX_THREADS
5+
/* This means pthreads are not implemented in libc headers, hence the macro
6+
not present in unistd.h. But they still can be implemented as an external
7+
library (e.g. gnu pth in pthread emulation) */
8+
# ifdef HAVE_PTHREAD_H
9+
# include <pthread.h> /* _POSIX_THREADS */
10+
# endif
11+
#endif
12+
13+
#ifdef _POSIX_THREADS
14+
/*
15+
* POSIX support
16+
*/
17+
#define Py_HAVE_CONDVAR
18+
19+
#include <pthread.h>
20+
21+
#define PyMUTEX_T pthread_mutex_t
22+
#define PyCOND_T pthread_cond_t
23+
24+
#elif defined(NT_THREADS)
25+
/*
26+
* Windows (XP, 2003 server and later, as well as (hopefully) CE) support
27+
*
28+
* Emulated condition variables ones that work with XP and later, plus
29+
* example native support on VISTA and onwards.
30+
*/
31+
#define Py_HAVE_CONDVAR
32+
33+
/* include windows if it hasn't been done before */
34+
#define WIN32_LEAN_AND_MEAN
35+
#include <windows.h>
36+
37+
/* options */
38+
/* non-emulated condition variables are provided for those that want
39+
* to target Windows Vista. Modify this macro to enable them.
40+
*/
41+
#ifndef _PY_EMULATED_WIN_CV
42+
#define _PY_EMULATED_WIN_CV 1 /* use emulated condition variables */
43+
#endif
44+
45+
/* fall back to emulation if not targeting Vista */
46+
#if !defined NTDDI_VISTA || NTDDI_VERSION < NTDDI_VISTA
47+
#undef _PY_EMULATED_WIN_CV
48+
#define _PY_EMULATED_WIN_CV 1
49+
#endif
50+
51+
#if _PY_EMULATED_WIN_CV
52+
53+
typedef CRITICAL_SECTION PyMUTEX_T;
54+
55+
/* The ConditionVariable object. From XP onwards it is easily emulated
56+
with a Semaphore.
57+
Semaphores are available on Windows XP (2003 server) and later.
58+
We use a Semaphore rather than an auto-reset event, because although
59+
an auto-resent event might appear to solve the lost-wakeup bug (race
60+
condition between releasing the outer lock and waiting) because it
61+
maintains state even though a wait hasn't happened, there is still
62+
a lost wakeup problem if more than one thread are interrupted in the
63+
critical place. A semaphore solves that, because its state is
64+
counted, not Boolean.
65+
Because it is ok to signal a condition variable with no one
66+
waiting, we need to keep track of the number of
67+
waiting threads. Otherwise, the semaphore's state could rise
68+
without bound. This also helps reduce the number of "spurious wakeups"
69+
that would otherwise happen.
70+
*/
71+
72+
typedef struct _PyCOND_T
73+
{
74+
HANDLE sem;
75+
int waiting; /* to allow PyCOND_SIGNAL to be a no-op */
76+
} PyCOND_T;
77+
78+
#else /* !_PY_EMULATED_WIN_CV */
79+
80+
/* Use native Win7 primitives if build target is Win7 or higher */
81+
82+
/* SRWLOCK is faster and better than CriticalSection */
83+
typedef SRWLOCK PyMUTEX_T;
84+
85+
typedef CONDITION_VARIABLE PyCOND_T;
86+
87+
#endif /* _PY_EMULATED_WIN_CV */
88+
89+
#endif /* _POSIX_THREADS, NT_THREADS */
90+
91+
#endif /* _CONDVAR_H_ */

Include/internal/_gil.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef _Py_GIL_H
2+
#define _Py_GIL_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#include "pyatomic.h"
8+
9+
#include "internal/_condvar.h"
10+
#ifndef Py_HAVE_CONDVAR
11+
#error You need either a POSIX-compatible or a Windows system!
12+
#endif
13+
14+
/* Enable if you want to force the switching of threads at least
15+
every `interval`. */
16+
#undef FORCE_SWITCHING
17+
#define FORCE_SWITCHING
18+
19+
struct _gil_runtime_state {
20+
/* microseconds (the Python API uses seconds, though) */
21+
unsigned long interval;
22+
/* Last PyThreadState holding / having held the GIL. This helps us
23+
know whether anyone else was scheduled after we dropped the GIL. */
24+
_Py_atomic_address last_holder;
25+
/* Whether the GIL is already taken (-1 if uninitialized). This is
26+
atomic because it can be read without any lock taken in ceval.c. */
27+
_Py_atomic_int locked;
28+
/* Number of GIL switches since the beginning. */
29+
unsigned long switch_number;
30+
#ifdef WITH_THREAD
31+
/* This condition variable allows one or several threads to wait
32+
until the GIL is released. In addition, the mutex also protects
33+
the above variables. */
34+
PyCOND_T cond;
35+
PyMUTEX_T mutex;
36+
#ifdef FORCE_SWITCHING
37+
/* This condition variable helps the GIL-releasing thread wait for
38+
a GIL-awaiting thread to be scheduled and take the GIL. */
39+
PyCOND_T switch_cond;
40+
PyMUTEX_T switch_mutex;
41+
#endif
42+
#endif /* WITH_THREAD */
43+
};
44+
45+
#ifdef __cplusplus
46+
}
47+
#endif
48+
#endif /* !_Py_GIL_H */

0 commit comments

Comments
 (0)