Skip to content

Commit fc324bd

Browse files
committed
Revert "[3.15] gh-158552: Wait for Windows threads to suspend before blocking sampling (GH-158802) (#158845)"
This reverts commit 31288da.
1 parent 3ba36b6 commit fc324bd

4 files changed

Lines changed: 0 additions & 150 deletions

File tree

‎Lib/test/test_profiling/test_sampling_profiler/test_blocking.py‎

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import subprocess
66
import sys
77
import textwrap
8-
import time
98
import unittest
109
from unittest import mock
1110

@@ -214,59 +213,3 @@ def test_run_blocking_exits_after_target_process_exits(self):
214213
replay.returncode, 0,
215214
f"stdout:\n{replay.stdout}\nstderr:\n{replay.stderr}",
216215
)
217-
218-
219-
@requires_remote_subprocess_debugging()
220-
@unittest.skipUnless(sys.platform == "win32", "Windows only")
221-
class TestBlockingModeSuspension(unittest.TestCase):
222-
def test_all_threads_stop_before_pause_returns(self):
223-
import mmap
224-
225-
tag = f"cpython_blocking_{os.getpid()}_{id(self)}"
226-
script = textwrap.dedent(f'''
227-
import mmap
228-
import struct
229-
import threading
230-
231-
memory = mmap.mmap(-1, 16, tagname={tag!r})
232-
233-
def worker(offset):
234-
counter = 0
235-
while True:
236-
counter += 1
237-
struct.pack_into("q", memory, offset, counter)
238-
239-
for offset in (0, 8):
240-
threading.Thread(target=worker, args=(offset,), daemon=True).start()
241-
_test_sock.sendall(b"working")
242-
_test_sock.recv(1)
243-
''')
244-
with mmap.mmap(-1, 16, tagname=tag) as memory:
245-
with test_subprocess(script, wait_for_working=True) as subproc:
246-
unwinder = _remote_debugging.RemoteUnwinder(
247-
subproc.process.pid, all_threads=True)
248-
deadline = time.monotonic() + SHORT_TIMEOUT
249-
while not all(memory[offset:offset + 8] != bytes(8)
250-
for offset in (0, 8)):
251-
self.assertLess(time.monotonic(), deadline,
252-
"Worker threads did not start")
253-
time.sleep(0.001)
254-
for _ in range(100):
255-
self.assertTrue(unwinder.pause_threads())
256-
try:
257-
before = memory[:]
258-
self.assertFalse(unwinder.pause_threads())
259-
unwinder.get_stack_trace()
260-
time.sleep(0.001)
261-
self.assertEqual(memory[:], before,
262-
"Target memory changed while paused")
263-
finally:
264-
unwinder.resume_threads()
265-
self.assertFalse(unwinder.resume_threads())
266-
before = memory[:]
267-
deadline = time.monotonic() + SHORT_TIMEOUT
268-
while any(memory[offset:offset + 8] == before[offset:offset + 8]
269-
for offset in (0, 8)):
270-
self.assertLess(time.monotonic(), deadline,
271-
"Worker threads did not resume")
272-
time.sleep(0.001)

‎Misc/NEWS.d/next/Library/2026-10-04-20-00-00.gh-issue-158552.windows.rst‎

Lines changed: 0 additions & 3 deletions
This file was deleted.

‎Modules/_remote_debugging/_remote_debugging.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ typedef struct {
105105
#ifdef MS_WINDOWS
106106
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
107107
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
108-
#define STATUS_NO_MORE_ENTRIES ((NTSTATUS)0x8000001AL)
109108
typedef enum _WIN32_THREADSTATE {
110109
WIN32_THREADSTATE_INITIALIZED = 0,
111110
WIN32_THREADSTATE_READY = 1,

‎Modules/_remote_debugging/threads.c‎

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -828,89 +828,6 @@ _Py_RemoteDebug_ResumeAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug
828828

829829
#elif defined(MS_WINDOWS)
830830

831-
static int
832-
wait_for_threads_to_stop(RemoteUnwinderObject *unwinder)
833-
{
834-
typedef NTSTATUS (NTAPI *NtGetNextThreadFunc)(
835-
HANDLE, HANDLE, ACCESS_MASK, ULONG, ULONG, PHANDLE);
836-
static NtGetNextThreadFunc pNtGetNextThread = NULL;
837-
static int tried_load = 0;
838-
839-
if (!tried_load) {
840-
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
841-
if (hNtdll) {
842-
pNtGetNextThread = (NtGetNextThreadFunc)GetProcAddress(
843-
hNtdll, "NtGetNextThread");
844-
}
845-
tried_load = 1;
846-
}
847-
if (pNtGetNextThread == NULL) {
848-
PyErr_SetString(PyExc_RuntimeError, "NtGetNextThread not available");
849-
return -1;
850-
}
851-
852-
HANDLE previous = NULL;
853-
int result = -1;
854-
for (;;) {
855-
HANDLE next = NULL;
856-
// Enumerate with the available access, then obtain context access
857-
// separately so threads that deny it are not silently skipped.
858-
NTSTATUS status = pNtGetNextThread(unwinder->handle.hProcess,
859-
previous, MAXIMUM_ALLOWED, 0, 0, &next);
860-
if (previous != NULL) {
861-
CloseHandle(previous);
862-
}
863-
previous = next;
864-
if (status == STATUS_NO_MORE_ENTRIES) {
865-
break;
866-
}
867-
if (status < 0) {
868-
if (!is_process_alive(unwinder->handle.hProcess)) {
869-
PyErr_Format(PyExc_ProcessLookupError,
870-
"Process %d has terminated", unwinder->handle.pid);
871-
}
872-
else {
873-
PyErr_Format(PyExc_RuntimeError,
874-
"NtGetNextThread failed: 0x%lx", status);
875-
}
876-
goto done;
877-
}
878-
HANDLE thread;
879-
if (!DuplicateHandle(GetCurrentProcess(), next, GetCurrentProcess(),
880-
&thread, THREAD_GET_CONTEXT | SYNCHRONIZE,
881-
FALSE, 0)) {
882-
PyErr_SetFromWindowsErr(GetLastError());
883-
goto done;
884-
}
885-
// Suspension is asynchronous. Reading the context waits for the
886-
// thread to stop before we start reading the target's memory.
887-
CONTEXT context = {.ContextFlags = CONTEXT_CONTROL};
888-
if (!GetThreadContext(thread, &context)) {
889-
DWORD error = GetLastError();
890-
int exited = WaitForSingleObject(thread, 0) == WAIT_OBJECT_0;
891-
CloseHandle(thread);
892-
if (exited) {
893-
continue;
894-
}
895-
PyErr_SetFromWindowsErr(error);
896-
goto done;
897-
}
898-
CloseHandle(thread);
899-
}
900-
if (!is_process_alive(unwinder->handle.hProcess)) {
901-
PyErr_Format(PyExc_ProcessLookupError,
902-
"Process %d has terminated", unwinder->handle.pid);
903-
goto done;
904-
}
905-
result = 0;
906-
907-
done:
908-
if (previous != NULL) {
909-
CloseHandle(previous);
910-
}
911-
return result;
912-
}
913-
914831
void
915832
_Py_RemoteDebug_InitThreadsState(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st)
916833
{
@@ -941,12 +858,6 @@ _Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_T
941858
if (status >= 0) {
942859
st->hProcess = unwinder->handle.hProcess;
943860
st->suspended = 1;
944-
if (wait_for_threads_to_stop(unwinder) < 0) {
945-
// pause_threads() has not yet set threads_stopped, so its caller
946-
// will not resume the process when we return an error.
947-
_Py_RemoteDebug_ResumeAllThreads(unwinder, st);
948-
return -1;
949-
}
950861
_Py_RemoteDebug_ClearCache(&unwinder->handle);
951862
return 0;
952863
}

0 commit comments

Comments
 (0)