Skip to content

Commit 2ebc5ce

Browse files
bpo-30860: Consolidate stateful runtime globals. (#3397)
* 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 bab21fa commit 2ebc5ce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+2750
-1316
lines changed

Include/ceval.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void);
9393
PyThreadState_GET()->overflowed = 0; \
9494
} while(0)
9595
PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
96+
/* XXX _Py_CheckRecursionLimit should be changed to
97+
_PyRuntime.ceval.check_recursion_limit. However, due to the macros
98+
in which it's used, _Py_CheckRecursionLimit is stuck in the stable
99+
ABI. It should be removed therefrom when possible.
100+
*/
96101
PyAPI_DATA(int) _Py_CheckRecursionLimit;
97102

98103
#ifdef USE_STACKCHECK

Include/internal/ceval.h

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

Include/internal/gil.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef Py_INTERNAL_GIL_H
2+
#define Py_INTERNAL_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+
/* This condition variable allows one or several threads to wait
31+
until the GIL is released. In addition, the mutex also protects
32+
the above variables. */
33+
PyCOND_T cond;
34+
PyMUTEX_T mutex;
35+
#ifdef FORCE_SWITCHING
36+
/* This condition variable helps the GIL-releasing thread wait for
37+
a GIL-awaiting thread to be scheduled and take the GIL. */
38+
PyCOND_T switch_cond;
39+
PyMUTEX_T switch_mutex;
40+
#endif
41+
};
42+
43+
#ifdef __cplusplus
44+
}
45+
#endif
46+
#endif /* !Py_INTERNAL_GIL_H */

0 commit comments

Comments
 (0)