You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
concurrent.interpreters.Queue.get()/put() mishandle the timeout argument #153005
concurrent.interpreters.Queue.get() and Queue.put() mishandle their timeout argument in three related ways.
The value is converted with int(timeout), which truncates a floating-point
timeout to whole seconds. A timeout in the interval [0, 1) becomes a
non-blocking call, and e.g. timeout=1.9 waits for only about one second. queue.Queue, which this queue is meant to be compatible with, accepts a
floating-point number of seconds.
Because of the same int() conversion, a small negative float such as timeout=-0.5 is truncated to 0 and passes the timeout < 0 check
instead of raising ValueError.
The deadline is computed with time.time() (the wall clock), while queue.Queue uses time.monotonic(). The timeout can therefore over- or
under-wait if the system clock is adjusted (NTP step, manual change) during
the call.
Reproducer (3.14.5)
importtimefromconcurrent.interpretersimportcreate_queueq=create_queue(maxsize=1)
q.put(b"x") # fill itstart=time.perf_counter()
try:
q.put(b"y", timeout=0.5) # expected: block ~0.5s, then QueueFullexceptExceptionasexc:
print(type(exc).__name__, f"{(time.perf_counter() -start) *1000:.1f} ms")
# -> QueueFull 0.0 ms (expected ~500 ms)q2=create_queue()
start=time.perf_counter()
try:
q2.get(timeout=0.9) # expected: block ~0.9s, then QueueEmptyexceptExceptionasexc:
print(type(exc).__name__, f"{(time.perf_counter() -start) *1000:.1f} ms")
# -> QueueEmpty 0.0 ms (expected ~900 ms)
For comparison, queue.Queue().get(timeout=0.5) blocks for about 500 ms.
The fix is to use the timeout value as given, reject a negative or NaN timeout
with ValueError, and base the deadline on time.monotonic().
As for the truncation to int, I would actually leave it as is for now. We need to discuss why it was like this first. And possibly change the docs as well. What about other queues implementations in the stdlib?
The other queues in the stdlib all accept a float timeout and none of them truncate it:
Activity
Merge branch 'main' into fix-pythongh-153005-interpreters-queue-monot…
gh-153005: Use a monotonic clock for concurrent.interpreters Queue ti…
pythongh-153005: Use a monotonic clock for concurrent.interpreters Qu…
[3.14] gh-153005: Use a monotonic clock for concurrent.interpreters Q…
[3.13] gh-153005: Use a monotonic clock for concurrent.interpreters Q…
[3.15] gh-153005: Use a monotonic clock for concurrent.interpreters Q…
aisk commentedon Oct 13, 2026
@picnixz on #153006 (comment):
The other queues in the stdlib all accept a float timeout and none of them truncate it:
queue.Queue.get(): https://github.com/python/cpython/blob/83dbe6ae9f47/Lib/queue.py#L202-L212queue.SimpleQueue.get(): https://github.com/python/cpython/blob/83dbe6ae9f47/Modules/_queuemodule.c#L384-L397multiprocessing.Queue.get(): https://github.com/python/cpython/blob/83dbe6ae9f47/Lib/multiprocessing/queues.py#L96-L111I checked the code, and
concurrent.interpreters.Queuecan support a float timeout. The tests added with the original implementation also use a float as the timeout: https://github.com/python/cpython/blob/83dbe6ae9f47/Lib/test/test_interpreters/test_queues.py#L314. So I guess theint()conversion was just intended to check the type of the timeout, andfloat()would have worked as well.