https://github.com/python/cpython/commit/b205580c37b765f212b961c3f1119ba74f5a7099
commit: b205580c37b765f212b961c3f1119ba74f5a7099
branch: 3.13
author: Bhuvansh <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-20T19:46:38+03:00
summary:

[3.13] gh-153005: Use a monotonic clock for concurrent.interpreters Queue 
timeouts (GH-154156) (GH-156019)

Queue.get() and Queue.put() computed their timeout deadline from
time.time(), the wall clock. If the system clock was stepped (NTP, a
manual change) while a call was blocked, the timeout could over- or
under-wait. queue.Queue uses time.monotonic() for the same reason.

Compute the deadline and check it against time.monotonic() instead.

(cherry picked from commit b94b9c8886a987a324a677b5fda5bef27f15cb14)

Co-authored-by: Vyron Vasileiadis <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-07-19-17-45-00.gh-issue-153005.F6WH92.rst
M Lib/test/support/interpreters/queues.py
M Lib/test/test_interpreters/test_queues.py

diff --git a/Lib/test/support/interpreters/queues.py 
b/Lib/test/support/interpreters/queues.py
index deb8e8613af731..c5b1fc9dd31bbf 100644
--- a/Lib/test/support/interpreters/queues.py
+++ b/Lib/test/support/interpreters/queues.py
@@ -230,14 +230,14 @@ def put(self, obj, timeout=None, *,
             timeout = int(timeout)
             if timeout < 0:
                 raise ValueError(f'timeout value must be non-negative')
-            end = time.time() + timeout
+            end = time.monotonic() + timeout
         if fmt is _PICKLED:
             obj = pickle.dumps(obj)
         while True:
             try:
                 _queues.put(self._id, obj, fmt, unboundop)
             except QueueFull as exc:
-                if timeout is not None and time.time() >= end:
+                if timeout is not None and time.monotonic() >= end:
                     raise  # re-raise
                 time.sleep(_delay)
             else:
@@ -271,12 +271,12 @@ def get(self, timeout=None, *,
             timeout = int(timeout)
             if timeout < 0:
                 raise ValueError(f'timeout value must be non-negative')
-            end = time.time() + timeout
+            end = time.monotonic() + timeout
         while True:
             try:
                 obj, fmt, unboundop = _queues.get(self._id)
             except QueueEmpty as exc:
-                if timeout is not None and time.time() >= end:
+                if timeout is not None and time.monotonic() >= end:
                     raise  # re-raise
                 time.sleep(_delay)
             else:
diff --git a/Lib/test/test_interpreters/test_queues.py 
b/Lib/test/test_interpreters/test_queues.py
index 7858c4e602ce2b..f05ef46f761b57 100644
--- a/Lib/test/test_interpreters/test_queues.py
+++ b/Lib/test/test_interpreters/test_queues.py
@@ -2,8 +2,9 @@
 import pickle
 import threading
 from textwrap import dedent
-import unittest
 import time
+import unittest
+from unittest import mock
 
 from test.support import import_helper, Py_DEBUG
 # Raise SkipTest if subinterpreters not supported.
@@ -383,6 +384,19 @@ def test_get_timeout(self):
         with self.assertRaises(queues.QueueEmpty):
             queue.get(timeout=0.1)
 
+    def test_timeout_uses_monotonic_clock(self):
+        # gh-153005: the deadline must be computed from the monotonic clock,
+        # since the wall clock can be adjusted while the call is blocked.
+        queue = queues.create(1)
+        with mock.patch.object(queues, 'time', wraps=time) as fake_time:
+            with self.assertRaises(queues.QueueEmpty):
+                queue.get(timeout=0)
+            queue.put(None)
+            with self.assertRaises(queues.QueueFull):
+                queue.put(None, timeout=0)
+        fake_time.monotonic.assert_called()
+        fake_time.time.assert_not_called()
+
     def test_get_nowait(self):
         queue = queues.create()
         with self.assertRaises(queues.QueueEmpty):
diff --git 
a/Misc/NEWS.d/next/Library/2026-07-19-17-45-00.gh-issue-153005.F6WH92.rst 
b/Misc/NEWS.d/next/Library/2026-07-19-17-45-00.gh-issue-153005.F6WH92.rst
new file mode 100644
index 00000000000000..7f3d10b3072ef9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-19-17-45-00.gh-issue-153005.F6WH92.rst
@@ -0,0 +1,4 @@
+:meth:`!concurrent.interpreters.Queue.get` and
+:meth:`!concurrent.interpreters.Queue.put` now compute their ``timeout``
+deadline from :func:`time.monotonic` instead of the wall clock, so adjusting
+the system clock during the call no longer makes them over- or under-wait.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to