kirr has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/40092?usp=email )
Change subject: trx_toolkit/_clck_gen: Unroll our own timerfd_* functions ......................................................................
trx_toolkit/_clck_gen: Unroll our own timerfd_* functions
Iaa675c95059ec8ccfad667f69984d5a7f608c249 (trx_toolkit/clck_gen: Don't use threads because Python GIL is latency killer) switched CLCKGen to use timerfd and noted
Unfortunately os.timerfd_create() & friends are only available starting from Python 3.13 . If this poses a problem it can be easily solved by doing those timerfd related system calls in Cython, after we switch most of the codebase to Cython later.
As we are currently using py3.11 on the testing infrastructure it makes sense not to force us to upgrade to py3.13 there.
-> Unroll our own timerfd_* wrappers so that clck_gen and fake_trx work again on all py3 versions, not only on py3 ≥ 3.13 .
Change-Id: I39e4e05ba65fcb3e07bcfe19a3ef95201aaa40d0 --- M src/target/trx_toolkit/_clck_gen.pyx 1 file changed, 49 insertions(+), 4 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/92/40092/1
diff --git a/src/target/trx_toolkit/_clck_gen.pyx b/src/target/trx_toolkit/_clck_gen.pyx index 3a6bfc7..dbd3c75 100644 --- a/src/target/trx_toolkit/_clck_gen.pyx +++ b/src/target/trx_toolkit/_clck_gen.pyx @@ -53,7 +53,7 @@ self._t_tick = int(self.ctr_interval // ns)
# Initialize timer fd - self._timerfd = os.timerfd_create(time.CLOCK_MONOTONIC) + self._timerfd = _os_timerfd_create(time.CLOCK_MONOTONIC)
# (Optional) clock consumer self.clck_handler = None @@ -63,7 +63,7 @@
@property def running(self): - t_next, _ = os.timerfd_gettime_ns(self._timerfd) + t_next, _ = _os_timerfd_gettime_ns(self._timerfd) return (t_next != 0)
def start(self): @@ -71,11 +71,11 @@ self.clck_src = self.clck_start
# start timer fd - os.timerfd_settime_ns(self._timerfd, initial=self._t_tick, interval=self._t_tick) + _os_timerfd_settime_ns(self._timerfd, flags=0, initial=self._t_tick, interval=self._t_tick)
def stop(self): # stop timer fd - os.timerfd_settime_ns(self._timerfd, initial=0, interval=0) + _os_timerfd_settime_ns(self._timerfd, flags=0, initial=0, interval=0)
# tick must be called periodically by CLCKGen user. # @@ -129,3 +129,48 @@
# Increase frame count (modular arithmetic) self.clck_src = (self.clck_src + 1) % GSM_HYPERFRAME + + +# ---- misc ---- + +# _os_timerfd_<func> is like os.timerfd_<func> but works on all python versions. +# we do it ourselves because os.timerfd_* is available only for py ≥ 3.13 + +from posix.time cimport timespec, itimerspec + +cdef extern from "<sys/timerfd.h>": + int timerfd_create(int clockid, int flags) + int timerfd_settime(int fd, int flags, const itimerspec *new_value, itimerspec *old_value) + int timerfd_gettime(int fd, itimerspec *curr_value) + + +cdef int _os_timerfd_create(int clockid, int flags=0) except -1: + fd = timerfd_create(clockid, flags) + if fd == -1: + _raise_oserr() + return fd + +cdef _os_timerfd_settime_ns(int fd, flags=0, initial=0, interval=0): + cdef itimerspec its + its.it_value .tv_sec = initial // 1000000000 # 1e9 + its.it_value .tv_nsec = initial % 1000000000 + its.it_interval .tv_sec = interval // 1000000000 + its.it_interval .tv_nsec = interval % 1000000000 + err = timerfd_settime(fd, flags, &its, NULL) + if err == -1: + _raise_oserr() + +cdef _os_timerfd_gettime_ns(int fd): # -> (next_expiration, interval) + cdef itimerspec its + err = timerfd_gettime(fd, &its) + if err == -1: + _raise_oserr() + cdef object t_next = 0L # py long to avoid int overflow + cdef object t_interval = 0L + t_next += its.it_value.tv_sec + t_next *= 1000000000 # 1e9 + t_next += its.it_value.tv_nsec + t_interval += its.it_interval.tv_sec + t_interval *= 1000000000 # 1e9 + t_interval += its.it_interval.tv_nsec + return (t_next, t_interval)