Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit441affc

Browse files
authored
gh-111964: Implement stop-the-world pauses (gh-112471)
The `--disable-gil` builds occasionally need to pause all but one thread. Someexamples include:* Cyclic garbage collection, where this is often called a "stop the world event"* Before calling `fork()`, to ensure a consistent state for internal data structures* During interpreter shutdown, to ensure that daemon threads aren't accessing Python objectsThis adds the following functions to implement global and per-interpreter pauses:* `_PyEval_StopTheWorldAll()` and `_PyEval_StartTheWorldAll()` (for the global runtime)* `_PyEval_StopTheWorld()` and `_PyEval_StartTheWorld()` (per-interpreter)(The function names may change.)These functions are no-ops outside of the `--disable-gil` build.
1 parent5f19978 commit441affc

File tree

10 files changed

+336
-29
lines changed

10 files changed

+336
-29
lines changed

‎Include/cpython/pystate.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ struct _ts {
102102
#endif
103103
int_whence;
104104

105-
/* Thread state (_Py_THREAD_ATTACHED, _Py_THREAD_DETACHED,_Py_THREAD_GC).
105+
/* Thread state (_Py_THREAD_ATTACHED, _Py_THREAD_DETACHED,_Py_THREAD_SUSPENDED).
106106
See Include/internal/pycore_pystate.h for more details. */
107107
intstate;
108108

‎Include/internal/pycore_ceval.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ void _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame)
205205
#define_PY_CALLS_TO_DO_BIT 2
206206
#define_PY_ASYNC_EXCEPTION_BIT 3
207207
#define_PY_GC_SCHEDULED_BIT 4
208+
#define_PY_EVAL_PLEASE_STOP_BIT 5
208209

209210
/* Reserve a few bits for future use */
210211
#define_PY_EVAL_EVENTS_BITS 8

‎Include/internal/pycore_interp.h‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ struct _Py_long_state {
4141
intmax_str_digits;
4242
};
4343

44+
// Support for stop-the-world events. This exists in both the PyRuntime struct
45+
// for global pauses and in each PyInterpreterState for per-interpreter pauses.
46+
struct_stoptheworld_state {
47+
PyMutexmutex;// Serializes stop-the-world attempts.
48+
49+
// NOTE: The below fields are protected by HEAD_LOCK(runtime), not by the
50+
// above mutex.
51+
boolrequested;// Set when a pause is requested.
52+
boolworld_stopped;// Set when the world is stopped.
53+
boolis_global;// Set when contained in PyRuntime struct.
54+
55+
PyEventstop_event;// Set when thread_countdown reaches zero.
56+
Py_ssize_tthread_countdown;// Number of threads that must pause.
57+
58+
PyThreadState*requester;// Thread that requested the pause (may be NULL).
59+
};
4460

4561
/* cross-interpreter data registry */
4662

@@ -166,6 +182,7 @@ struct _is {
166182

167183
struct_warnings_runtime_statewarnings;
168184
structatexit_stateatexit;
185+
struct_stoptheworld_statestoptheworld;
169186

170187
#if defined(Py_GIL_DISABLED)
171188
struct_mimalloc_interp_statemimalloc;

‎Include/internal/pycore_llist.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ struct llist_node {
3737
};
3838

3939
// Get the struct containing a node.
40-
#definellist_data(node,type,member) \
41-
(type*)((char*)node - offsetof(type, member))
40+
#definellist_data(node,type,member) (_Py_CONTAINER_OF(node, type, member))
4241

4342
// Iterate over a list.
4443
#definellist_for_each(node,head) \

‎Include/internal/pycore_pystate.h‎

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,27 @@ extern "C" {
2121
// interpreter at the same time. Only the "bound" thread may perform the
2222
// transitions between "attached" and "detached" on its own PyThreadState.
2323
//
24-
// The "gc" state is used to implement stop-the-world pauses, such as for
25-
// cyclic garbage collection. It is only used in `--disable-gil` builds. It is
26-
// similar to the "detached" state, but only the thread performing a
27-
//stop-the-world pause may transition threads between the "detached" and "gc"
28-
//states. A thread trying to "attach" from the "gc" state will block until
29-
//it is transitioned back to "detached" when thestop-the-world pauseis
30-
//complete.
24+
// The "suspended" state is used to implement stop-the-world pauses, such as
25+
//forcyclic garbage collection. It is only used in `--disable-gil` builds.
26+
//The "suspended" state issimilar to the "detached" state in that in both
27+
//statesthe thread is not allowed to call most Python APIs. However, unlike
28+
//the "detached" state, a thread may not transition itself out from the
29+
//"suspended" state. Only the thread performing astop-the-world pausemay
30+
//transition a thread from the "suspended" state back to the "detached" state.
3131
//
3232
// State transition diagram:
3333
//
3434
// (bound thread) (stop-the-world thread)
35-
// [attached] <-> [detached] <-> [gc]
35+
// [attached] <-> [detached] <-> [suspended]
36+
// | ^
37+
// +---------------------------->---------------------------+
38+
// (bound thread)
3639
//
37-
// See `_PyThreadState_Attach()` and `_PyThreadState_Detach()`.
40+
// The (bound thread) and (stop-the-world thread) labels indicate which thread
41+
// is allowed to perform the transition.
3842
#define_Py_THREAD_DETACHED 0
3943
#define_Py_THREAD_ATTACHED 1
40-
#define_Py_THREAD_GC 2
44+
#define_Py_THREAD_SUSPENDED 2
4145

4246

4347
/* Check if the current thread is the main thread.
@@ -140,13 +144,36 @@ _PyThreadState_GET(void)
140144
//
141145
// High-level code should generally call PyEval_RestoreThread() instead, which
142146
// calls this function.
143-
void_PyThreadState_Attach(PyThreadState*tstate);
147+
externvoid_PyThreadState_Attach(PyThreadState*tstate);
144148

145149
// Detaches the current thread from the interpreter.
146150
//
147151
// High-level code should generally call PyEval_SaveThread() instead, which
148152
// calls this function.
149-
void_PyThreadState_Detach(PyThreadState*tstate);
153+
externvoid_PyThreadState_Detach(PyThreadState*tstate);
154+
155+
// Detaches the current thread to the "suspended" state if a stop-the-world
156+
// pause is in progress.
157+
//
158+
// If there is no stop-the-world pause in progress, then the thread switches
159+
// to the "detached" state.
160+
externvoid_PyThreadState_Suspend(PyThreadState*tstate);
161+
162+
// Perform a stop-the-world pause for all threads in the all interpreters.
163+
//
164+
// Threads in the "attached" state are paused and transitioned to the "GC"
165+
// state. Threads in the "detached" state switch to the "GC" state, preventing
166+
// them from reattaching until the stop-the-world pause is complete.
167+
//
168+
// NOTE: This is a no-op outside of Py_GIL_DISABLED builds.
169+
externvoid_PyEval_StopTheWorldAll(_PyRuntimeState*runtime);
170+
externvoid_PyEval_StartTheWorldAll(_PyRuntimeState*runtime);
171+
172+
// Perform a stop-the-world pause for threads in the specified interpreter.
173+
//
174+
// NOTE: This is a no-op outside of Py_GIL_DISABLED builds.
175+
externvoid_PyEval_StopTheWorld(PyInterpreterState*interp);
176+
externvoid_PyEval_StartTheWorld(PyInterpreterState*interp);
150177

151178

152179
staticinlinevoid

‎Include/internal/pycore_runtime.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,13 @@ typedef struct pyruntimestate {
227227
struct_faulthandler_runtime_statefaulthandler;
228228
struct_tracemalloc_runtime_statetracemalloc;
229229

230+
// The rwmutex is used to prevent overlapping global and per-interpreter
231+
// stop-the-world events. Global stop-the-world events lock the mutex
232+
// exclusively (as a "writer"), while per-interpreter stop-the-world events
233+
// lock it non-exclusively (as "readers").
234+
_PyRWMutexstoptheworld_mutex;
235+
struct_stoptheworld_statestoptheworld;
236+
230237
PyPreConfigpreconfig;
231238

232239
// Audit values must be preserved when Py_Initialize()/Py_Finalize()

‎Include/internal/pycore_runtime_init.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ extern PyTypeObject _PyExc_MemoryError;
116116
}, \
117117
.faulthandler = _faulthandler_runtime_state_INIT, \
118118
.tracemalloc = _tracemalloc_runtime_state_INIT, \
119+
.stoptheworld = { \
120+
.is_global = 1, \
121+
}, \
119122
.float_state = { \
120123
.float_format = _py_float_format_unknown, \
121124
.double_format = _py_float_format_unknown, \

‎Include/pymacro.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@
160160
Py_FatalError("Unreachable C code path reached")
161161
#endif
162162

163+
#define_Py_CONTAINER_OF(ptr,type,member) \
164+
(type*)((char*)ptr - offsetof(type, member))
165+
163166
// Prevent using an expression as a l-value.
164167
// For example, "int x; _Py_RVALUE(x) = 1;" fails with a compiler error.
165168
#define_Py_RVALUE(EXPR) ((void)0, (EXPR))

‎Python/ceval_gil.c‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,15 @@ _Py_HandlePending(PyThreadState *tstate)
949949
{
950950
PyInterpreterState*interp=tstate->interp;
951951

952+
/* Stop-the-world */
953+
if (_Py_eval_breaker_bit_is_set(interp,_PY_EVAL_PLEASE_STOP_BIT)) {
954+
_Py_set_eval_breaker_bit(interp,_PY_EVAL_PLEASE_STOP_BIT,0);
955+
_PyThreadState_Suspend(tstate);
956+
957+
/* The attach blocks until the stop-the-world event is complete. */
958+
_PyThreadState_Attach(tstate);
959+
}
960+
952961
/* Pending signals */
953962
if (_Py_eval_breaker_bit_is_set(interp,_PY_SIGNALS_PENDING_BIT)) {
954963
if (handle_signals(tstate)!=0) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp