From 1026d8df88f8523c1d2041fa3928b4252303fcad Mon Sep 17 00:00:00 2001 From: And Clover Date: Wed, 4 Mar 2020 14:42:16 +0000 Subject: [PATCH] bpo-39847: don't over-wait for mutex on win32 at tickcount overflow The 32-bit (49-day) TickCount relied on in EnterNonRecursiveMutex can overflow in the gap between the 'target' time and the 'now' time WaitForSingleObjectEx returns, causing the loop to think it needs to wait another 49 days. This is most likely to happen when the machine is hibernated during WaitForSingleObjectEx. This makes acquiring a lock/event/etc from the _thread or threading module appear to never timeout. Replace with GetTickCount64 - this is OK now we no longer support XP which lacks it, and is in use for time.monotonic. Approaches not taken: - forcing signed arithmetic for the now/target comparison (this would only partially solve the problem with GetTickCount as you could still break it by hibernating for 24 days; it's unnecessary for 64-bit which takes however many hundred million years to roll over) --- .../next/Windows/2020-03-04-17-05-11.bpo-39847.C3N2m3.rst | 2 ++ Python/thread_nt.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2020-03-04-17-05-11.bpo-39847.C3N2m3.rst diff --git a/Misc/NEWS.d/next/Windows/2020-03-04-17-05-11.bpo-39847.C3N2m3.rst b/Misc/NEWS.d/next/Windows/2020-03-04-17-05-11.bpo-39847.C3N2m3.rst new file mode 100644 index 00000000000000..acfbce53eb3997 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-03-04-17-05-11.bpo-39847.C3N2m3.rst @@ -0,0 +1,2 @@ +Avoid hang when computer is hibernated whilst waiting for a mutex (for +lock-related objects from :mod:`threading`) around 49-day uptime. diff --git a/Python/thread_nt.h b/Python/thread_nt.h index a5246dd0504dbb..23d585cf9fa6c0 100644 --- a/Python/thread_nt.h +++ b/Python/thread_nt.h @@ -75,16 +75,16 @@ EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) } } else if (milliseconds != 0) { /* wait at least until the target */ - DWORD now, target = GetTickCount() + milliseconds; + ULONGLONG now, target = GetTickCount64() + milliseconds; while (mutex->locked) { if (PyCOND_TIMEDWAIT(&mutex->cv, &mutex->cs, (long long)milliseconds*1000) < 0) { result = WAIT_FAILED; break; } - now = GetTickCount(); + now = GetTickCount64(); if (target <= now) break; - milliseconds = target-now; + milliseconds = (DWORD)(target-now); } } if (!mutex->locked) {