Significantly reduce clock_gettime syscalls on platforms with broken vdso (#81257)

This commit is contained in:
J. Nick Koston 2022-10-31 08:35:08 -05:00 committed by GitHub
parent 8416cc1906
commit 1589c06203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 43 additions and 10 deletions

View File

@ -3,13 +3,13 @@ from __future__ import annotations
from collections.abc import Callable, Coroutine
import logging
import time
from typing import Any, Generic, TypeVar
from bleak import BleakError
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.debounce import Debouncer
from homeassistant.util.dt import monotonic_time_coarse
from . import BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak
from .passive_update_processor import PassiveBluetoothProcessorCoordinator
@ -94,7 +94,7 @@ class ActiveBluetoothProcessorCoordinator(
"""Return true if time to try and poll."""
poll_age: float | None = None
if self._last_poll:
poll_age = time.monotonic() - self._last_poll
poll_age = monotonic_time_coarse() - self._last_poll
return self._needs_poll_method(service_info, poll_age)
async def _async_poll_data(
@ -124,7 +124,7 @@ class ActiveBluetoothProcessorCoordinator(
self.last_poll_successful = False
return
finally:
self._last_poll = time.monotonic()
self._last_poll = monotonic_time_coarse()
if not self.last_poll_successful:
self.logger.debug("%s: Polling recovered")

View File

@ -7,7 +7,6 @@ from dataclasses import replace
from datetime import datetime, timedelta
import itertools
import logging
import time
from typing import TYPE_CHECKING, Any, Final
from bleak.backends.scanner import AdvertisementDataCallback
@ -22,6 +21,7 @@ from homeassistant.core import (
)
from homeassistant.helpers import discovery_flow
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.util.dt import monotonic_time_coarse
from .advertisement_tracker import AdvertisementTracker
from .const import (
@ -69,7 +69,7 @@ APPLE_START_BYTES_WANTED: Final = {
APPLE_DEVICE_ID_START_BYTE,
}
MONOTONIC_TIME: Final = time.monotonic
MONOTONIC_TIME: Final = monotonic_time_coarse
_LOGGER = logging.getLogger(__name__)

View File

@ -6,7 +6,6 @@ from collections.abc import Callable
from datetime import datetime
import logging
import platform
import time
from typing import Any
import async_timeout
@ -22,6 +21,7 @@ from dbus_fast import InvalidMessageError
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.util.dt import monotonic_time_coarse
from homeassistant.util.package import is_docker_env
from .const import (
@ -35,7 +35,7 @@ from .models import BaseHaScanner, BluetoothScanningMode, BluetoothServiceInfoBl
from .util import adapter_human_name, async_reset_adapter
OriginalBleakScanner = bleak.BleakScanner
MONOTONIC_TIME = time.monotonic
MONOTONIC_TIME = monotonic_time_coarse
# or_patterns is a workaround for the fact that passive scanning
# needs at least one matcher to be set. The below matcher

View File

@ -2,11 +2,11 @@
from __future__ import annotations
import platform
import time
from bluetooth_auto_recovery import recover_adapter
from homeassistant.core import callback
from homeassistant.util.dt import monotonic_time_coarse
from .const import (
DEFAULT_ADAPTER_BY_PLATFORM,
@ -29,7 +29,7 @@ async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBlea
bluez_dbus = BlueZDBusObjects()
await bluez_dbus.load()
now = time.monotonic()
now = monotonic_time_coarse()
return {
address: BluetoothServiceInfoBleak(
name=history.advertisement_data.local_name

View File

@ -19,6 +19,7 @@ from homeassistant.components.bluetooth import (
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.util.dt import monotonic_time_coarse
TWO_CHAR = re.compile("..")
@ -84,7 +85,7 @@ class ESPHomeScanner(BaseHaScanner):
@callback
def async_on_advertisement(self, adv: BluetoothLEAdvertisement) -> None:
"""Call the registered callback."""
now = time.monotonic()
now = monotonic_time_coarse()
address = ":".join(TWO_CHAR.findall("%012X" % adv.address)) # must be upper
name = adv.name
if prev_discovery := self._discovered_device_advertisement_datas.get(address):

View File

@ -4,7 +4,9 @@ from __future__ import annotations
import bisect
from contextlib import suppress
import datetime as dt
import platform
import re
import time
from typing import Any
import zoneinfo
@ -13,6 +15,7 @@ import ciso8601
DATE_STR_FORMAT = "%Y-%m-%d"
UTC = dt.timezone.utc
DEFAULT_TIME_ZONE: dt.tzinfo = dt.timezone.utc
CLOCK_MONOTONIC_COARSE = 6
# EPOCHORDINAL is not exposed as a constant
# https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L12
@ -461,3 +464,26 @@ def _datetime_ambiguous(dattim: dt.datetime) -> bool:
assert dattim.tzinfo is not None
opposite_fold = dattim.replace(fold=not dattim.fold)
return _datetime_exists(dattim) and dattim.utcoffset() != opposite_fold.utcoffset()
def __monotonic_time_coarse() -> float:
"""Return a monotonic time in seconds.
This is the coarse version of time_monotonic, which is faster but less accurate.
Since many arm64 and 32-bit platforms don't support VDSO with time.monotonic
because of errata, we can't rely on the kernel to provide a fast
monotonic time.
https://lore.kernel.org/lkml/20170404171826.25030-1-marc.zyngier@arm.com/
"""
return time.clock_gettime(CLOCK_MONOTONIC_COARSE)
monotonic_time_coarse = time.monotonic
with suppress(Exception):
if (
platform.system() == "Linux"
and abs(time.monotonic() - __monotonic_time_coarse()) < 1
):
monotonic_time_coarse = __monotonic_time_coarse

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from datetime import datetime, timedelta
import time
import pytest
@ -719,3 +720,8 @@ def test_find_next_time_expression_tenth_second_pattern_does_not_drift_entering_
assert (next_target - prev_target).total_seconds() == 60
assert next_target.second == 10
prev_target = next_target
def test_monotonic_time_coarse():
"""Test monotonic time coarse."""
assert abs(time.monotonic() - dt_util.monotonic_time_coarse()) < 1