From 8a3ea163ef75dd8b87e51960c6201c161d73d11e Mon Sep 17 00:00:00 2001 From: notmmao Date: Thu, 22 May 2025 17:43:50 +0800 Subject: [PATCH 1/3] Add high-precision sleep functionality Implements a cross-platform high-precision sleep function to replace the default sleep behavior. This change enhances the timing accuracy in the Transport Layer by utilizing specific implementations for Windows and Linux. The new function improves responsiveness and efficiency, especially in scenarios requiring precise timing. --- isotp/protocol.py | 5 ++-- isotp/sleep_ns.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 isotp/sleep_ns.py diff --git a/isotp/protocol.py b/isotp/protocol.py index 1a66208..56510e7 100755 --- a/isotp/protocol.py +++ b/isotp/protocol.py @@ -9,6 +9,7 @@ from isotp.can_message import CanMessage from isotp.tools import Timer, FiniteByteGenerator +from isotp.sleep_ns import precise_sleep import isotp.address import isotp.errors @@ -366,7 +367,7 @@ def __init__(self) -> None: self.listen_mode = False self.blocking_send = False self.logger_name = TransportLayer.LOGGER_NAME - self.wait_func = time.sleep + self.wait_func = precise_sleep def set(self, key: str, val: Any, validate: bool = True) -> None: param_alias: Dict[str, str] = { @@ -1611,7 +1612,7 @@ def _relay_thread_fn(self) -> None: else: # No data received. Sleep if user is not blocking if not self.events.stop_requested.is_set(): if not self.blocking_rxfn or diff < rx_timeout * 0.5: - time.sleep(max(0, min(self.sleep_time(), rx_timeout - diff))) + self.params.wait_func(max(0, min(self.sleep_time(), rx_timeout - diff))) def _main_thread_fn(self) -> None: """Internal function executed by the main thread. """ diff --git a/isotp/sleep_ns.py b/isotp/sleep_ns.py new file mode 100644 index 0000000..7a763b8 --- /dev/null +++ b/isotp/sleep_ns.py @@ -0,0 +1,67 @@ +import time +import ctypes +import sys +import ctypes +import ctypes.util +import time + +def precise_sleep(duration_sec): + """ + Cross-platform high-precision sleep function + :param duration_sec: Sleep time in seconds (can be float) + """ + if sys.platform == 'win32': + _precise_sleep_windows(duration_sec) + else: + _precise_sleep_linux(duration_sec) + +def _precise_sleep_windows(duration_sec): + """High-precision sleep implementation for Windows""" + kernel32 = ctypes.windll.kernel32 + + # Create a waitable timer + timer = kernel32.CreateWaitableTimerExW( + None, None, 0x00000002, 0x1F0003 + ) + + # Time unit is 100 nanoseconds, negative value means relative time + delay = ctypes.c_longlong(int(-duration_sec * 10000000)) + + # Set the timer + kernel32.SetWaitableTimer( + timer, ctypes.byref(delay), 0, None, None, False + ) + + # Wait for the timer to trigger + kernel32.WaitForSingleObject(timer, 0xFFFFFFFF) + + # Close the handle + kernel32.CloseHandle(timer) + +def _precise_sleep_linux(duration_sec): + """High-precision sleep implementation for Linux""" + try: + # Try to use clock_nanosleep (most precise) + librt = ctypes.CDLL(ctypes.util.find_library("rt"), use_errno=True) + + class timespec(ctypes.Structure): + _fields_ = [("tv_sec", ctypes.c_long), + ("tv_nsec", ctypes.c_long)] + + req = timespec() + req.tv_sec = int(duration_sec) + req.tv_nsec = int((duration_sec - req.tv_sec) * 1e9) + + CLOCK_MONOTONIC = 1 + result = librt.clock_nanosleep( + CLOCK_MONOTONIC, 0, ctypes.byref(req), None + ) + + if result != 0: + errno = ctypes.get_errno() + raise OSError(errno, f"clock_nanosleep failed with errno {errno}") + + except Exception as e: + # If clock_nanosleep fails, fallback to select + if duration_sec > 0: + time.sleep(duration_sec) From 3d36a901be0bcd5a751c2043b534874b286693c9 Mon Sep 17 00:00:00 2001 From: notmmao Date: Wed, 28 May 2025 14:40:51 +0800 Subject: [PATCH 2/3] fix: add type hit --- isotp/sleep_ns.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/isotp/sleep_ns.py b/isotp/sleep_ns.py index 7a763b8..18fe352 100644 --- a/isotp/sleep_ns.py +++ b/isotp/sleep_ns.py @@ -1,21 +1,10 @@ -import time -import ctypes import sys +import time import ctypes import ctypes.util -import time -def precise_sleep(duration_sec): - """ - Cross-platform high-precision sleep function - :param duration_sec: Sleep time in seconds (can be float) - """ - if sys.platform == 'win32': - _precise_sleep_windows(duration_sec) - else: - _precise_sleep_linux(duration_sec) -def _precise_sleep_windows(duration_sec): +def _precise_sleep_windows(duration_sec: float) -> None: """High-precision sleep implementation for Windows""" kernel32 = ctypes.windll.kernel32 @@ -38,7 +27,7 @@ def _precise_sleep_windows(duration_sec): # Close the handle kernel32.CloseHandle(timer) -def _precise_sleep_linux(duration_sec): +def _precise_sleep_linux(duration_sec: float)-> None: """High-precision sleep implementation for Linux""" try: # Try to use clock_nanosleep (most precise) @@ -65,3 +54,14 @@ class timespec(ctypes.Structure): # If clock_nanosleep fails, fallback to select if duration_sec > 0: time.sleep(duration_sec) + + +def precise_sleep(duration_sec: float)-> None: + """ + Cross-platform high-precision sleep function + :param duration_sec: Sleep time in seconds (can be float) + """ + if sys.platform == 'win32': + _precise_sleep_windows(duration_sec) + else: + _precise_sleep_linux(duration_sec) From b6622d78c37aae23b6d1e051d518e477fc0182e9 Mon Sep 17 00:00:00 2001 From: notmmao Date: Wed, 28 May 2025 15:09:17 +0800 Subject: [PATCH 3/3] fix: mypy type check --- isotp/sleep_ns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isotp/sleep_ns.py b/isotp/sleep_ns.py index 18fe352..bf2d33c 100644 --- a/isotp/sleep_ns.py +++ b/isotp/sleep_ns.py @@ -6,7 +6,7 @@ def _precise_sleep_windows(duration_sec: float) -> None: """High-precision sleep implementation for Windows""" - kernel32 = ctypes.windll.kernel32 + kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined] # Create a waitable timer timer = kernel32.CreateWaitableTimerExW(