Skip to content

gh-145703: Fix asyncio.BaseEventLoop low clock resolution#145706

Merged
kumaraditya303 merged 6 commits intopython:mainfrom
CaptainFlint:fix-issue-145703
Mar 14, 2026
Merged

gh-145703: Fix asyncio.BaseEventLoop low clock resolution#145706
kumaraditya303 merged 6 commits intopython:mainfrom
CaptainFlint:fix-issue-145703

Conversation

@CaptainFlint
Copy link
Contributor

@CaptainFlint CaptainFlint commented Mar 9, 2026

With this fix we make sure that even if the clock resolution becomes too small, the "next tick" will still be different from the current time, and the comparison remains valid.

Example: clock resolution = 1e-9, current uptime = 200 days (17280000 seconds). Floating point addition does not change the value, because the precision is too small. In this case the patch will use math.ulp(), which is the smallest possible increment that will modify the current value.

Konstantin Vlasov added 2 commits March 9, 2026 19:52
asyncio event loop uses monotonic timer which in many systems is the OS uptime.
With low enough clock resolution (often being 1e-09) and high enough uptime
(~194 days), adding the "clock tick" to the current time hits the floating
point precision limits and does not change the time value.

The comparison then returns invalid result, and tasks scheduled to trigger at
this exact moment are not triggered. They will be triggered only later, at the
next call.

This commit fixes the issue by making sure the "next tick" is adjusted to the
current time's floating point precision. Therefore, end_time is guaranteed to
be incremented.
* Fixed sorting of imports
* Fixed formatting in the NEWS entry
* Reworded the explanatory comment
@picnixz
Copy link
Member

picnixz commented Mar 9, 2026

/home/runner/work/cpython/cpython/Doc/build/NEWS:40: WARNING: py:class reference target not found: asyncio.BaseEventLoop [ref.class]

Oh so BaseEventLoop doesn't have a documented entry. Maybe you can directly refer to call_soon (https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_soon) ?

@CaptainFlint
Copy link
Contributor Author

Oh so BaseEventLoop doesn't have a documented entry. Maybe you can directly refer to call_soon (https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_soon) ?

I found an existing reference to call_soon in other NEWS entries and copied it. Looks like it worked.

@picnixz
Copy link
Member

picnixz commented Mar 9, 2026

If there are other methods that are affected by that, you can also mention them (I actually don't know if it's used by call_soon, I just assumed so because it's about callbacks)

@CaptainFlint
Copy link
Contributor Author

Well, the code that triggered the issue for me was using call_at. That implies that call_later is also affected, since they are basically the one.

I can't tell whether call_soon is affected, actually, it's using a different approach, and I'm not familiar with the asyncio code structure to quickly see how the scheduler would process it.

I think, then, it's better to mention only call_at and call_later as they are definitely affected.

Copy link
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not an asyncio expert so I'll leave it for the maintainers but this looks like the best alternative.

@CaptainFlint
Copy link
Contributor Author

Thanks a lot for your input and help! I really appreciate it!

@kumaraditya303
Copy link
Contributor

This looks good, we aren't backporting this to I am fine with getting this in and see if something breaks.

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonderful. Go ahead and merge.

(Are there other loops doing a similar thing elsewhere? In asyncio or outside it?)

@kumaraditya303 kumaraditya303 merged commit 77c06f3 into python:main Mar 14, 2026
51 checks passed
@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot s390x RHEL9 Refleaks 3.x (tier-3) has failed when building commit 77c06f3.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1589/builds/3089) and take a look at the build logs.
  4. Check if the failure is related to this commit (77c06f3) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1589/builds/3089

Failed tests:

  • test_profiling

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/var/lib/buildbot/worker/cstratak-rhel9-s390x/3.x.cstratak-rhel9-s390x.refleak/build/Lib/test/test_profiling/test_sampling_profiler/test_advanced.py", line 169, in test_native_frames_enabled
    self.assertFalse(any(stack.endswith(";<native>") for stack in stacks))
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: True is not false

@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 FreeBSD Refleaks 3.x (tier-3) has failed when building commit 77c06f3.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1613/builds/2977) and take a look at the build logs.
  4. Check if the failure is related to this commit (77c06f3) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1613/builds/2977

Test leaking resources:

  • test_events: memory blocks

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/test/support/__init__.py", line 947, in gc_collect
    gc.collect()
    ~~~~~~~~~~^^
ResourceWarning: unclosed <socket.socket fd=10, family=2, type=1, proto=6, laddr=('127.0.0.1', 62446), raddr=('127.0.0.1', 62447)>
Task was destroyed but it is pending!
task: <Task pending name='Task-1498' coro=<BaseSelectorEventLoop._accept_connection2() done, defined at /buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py:217> wait_for=<Future pending cb=[Task.task_wakeup()]>>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x844149610>:
Traceback (most recent call last):
  File "/buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py", line 873, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=10>
k


Traceback (most recent call last):
  File "/buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/test/support/__init__.py", line 947, in gc_collect
    gc.collect()
    ~~~~~~~~~~^^
ResourceWarning: unclosed <socket.socket fd=8, family=2, type=1, proto=6, laddr=('127.0.0.1', 59198), raddr=('127.0.0.1', 59199)>
Task was destroyed but it is pending!
task: <Task pending name='Task-1730' coro=<BaseSelectorEventLoop._accept_connection2() done, defined at /buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py:217> wait_for=<Future pending cb=[Task.task_wakeup()]>>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x8459deb10>:
Traceback (most recent call last):
  File "/buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py", line 873, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=8>
k


Traceback (most recent call last):
  File "/buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/test/support/__init__.py", line 947, in gc_collect
    gc.collect()
    ~~~~~~~~~~^^
ResourceWarning: unclosed <socket.socket fd=10, family=2, type=1, proto=6, laddr=('127.0.0.1', 62837), raddr=('127.0.0.1', 62838)>
Task was destroyed but it is pending!
task: <Task pending name='Task-3623' coro=<BaseSelectorEventLoop._accept_connection2() done, defined at /buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py:217> wait_for=<Future pending cb=[Task.task_wakeup()]>>
Warning -- Unraisable exception
Exception ignored while calling deallocator <function _SelectorTransport.__del__ at 0x844149610>:
Traceback (most recent call last):
  File "/buildbot/buildarea/3.x.ware-freebsd.refleak/build/Lib/asyncio/selector_events.py", line 873, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ResourceWarning: unclosed transport <_SelectorSocketTransport closing fd=10>
k

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants