Repository navigation
Description
BEEP BOOP! I am Copilot working on Alex's behalf:
On Windows with CPython 3.13 x64, a windows.winproxy call that fails with ERROR_INSUFFICIENT_BUFFER (122) can raise WinproxyError with winerror=0 when another Python thread is contending for the GIL. In a single-threaded loop, the same call reports 122. The error appears to be read after the native call rather than captured at the ctypes boundary.
A standalone reproducer using a valid current-process token:
import ctypes
import sys
import threading
import windows
import windows.generated_def as gdef
token = windows.current_process.token
size = gdef.DWORD()
stop = threading.Event()
def contend_for_gil():
while not stop.is_set():
sum(range(1000))
worker = threading.Thread(target=contend_for_gil)
old_interval = sys.getswitchinterval()
sys.setswitchinterval(0.00001)
try:
worker.start()
for _ in range(100000):
try:
windows.winproxy.GetTokenInformation(
token.handle, gdef.TokenUser, None, 0, ctypes.byref(size)
)
except OSError as exc:
if exc.winerror != 122:
print("unexpected winerror:", exc.winerror)
break
else:
print("unexpected success")
break
finally:
stop.set()
worker.join()
sys.setswitchinterval(old_interval)Expected: each failing call reports error 122. Observed: under GIL contention, the wrapper can report error 0. The issue is in preserving the native thread's last-error value until PythonForWindows runs its error check.
The native trace distinguishes this from a failure in PyEval_RestoreThread: that GIL-reacquisition path restores 122. The later _Py_HandlePending GIL handoff reaches SleepConditionVariableSRW, then BaseSetLastNTError(STATUS_SUCCESS) calls ntdll!RtlSetLastWin32Error(0), overwriting 122. These symbol names were resolved with the matching Microsoft kernelbase PDB. The reproducer deliberately uses a 10-microsecond switch interval to expose the race; it is not a measurement of occurrence frequency under the default interval.
Activity