Add osmo_timerfd_* functions for osmo_fd-wrapped timerfd

Linux offers file descriptor based periodic (interval) timers,
which can achieve a higher precision than our userspace based
timers and which can be slave'd to CLOCK_MONOTINIC or other clock
sources.  Let's add some code for osmo_fd wrapped versions that
integrate well with our select() abstraction.

The code has been used in osmo-bts-trx since June 2017 (change-id
I51b19adde14ebb7ef3bb863d45e06243c323e22e), and I'm just renaming
and moving it to libosmocore here.  After a merge, the osmo-bts
implementations can be removed in favor if this one.

Change-Id: Ibeffba7c997252c003723bcd5d14122c4ded2fe7
This commit is contained in:
Harald Welte 2017-12-03 16:13:39 +01:00
parent b82a407a49
commit ea4d8939af
3 changed files with 74 additions and 1 deletions

View File

@ -55,7 +55,7 @@ AC_SUBST(LTLDFLAGS_OSMOCODING)
dnl checks for header files
AC_HEADER_STDC
AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h netinet/tcp.h)
AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h sys/timerfd.h syslog.h ctype.h netinet/tcp.h)
# for src/conv.c
AC_FUNC_ALLOCA
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DLOPEN="$LIBS";LIBS=""])

View File

@ -6,6 +6,7 @@
#include <osmocom/core/linuxlist.h>
#include <stdbool.h>
#include <time.h>
/*! \defgroup select Select loop abstraction
* @{
@ -54,4 +55,10 @@ struct osmo_fd *osmo_fd_get_by_fd(int fd);
int osmo_fd_fill_fds(void *readset, void *writeset, void *exceptset);
int osmo_fd_disp_fds(void *readset, void *writeset, void *exceptset);
/* timerfd integration */
int osmo_timerfd_disable(struct osmo_fd *ofd);
int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
const struct timespec *interval);
int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data);
/*! @} */

View File

@ -30,6 +30,7 @@
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
@ -270,6 +271,71 @@ struct osmo_fd *osmo_fd_get_by_fd(int fd)
return NULL;
}
#ifdef HAVE_SYS_TIMERFD_H
#include <sys/timerfd.h>
/*! disable the osmocom-wrapped timerfd */
int osmo_timerfd_disable(struct osmo_fd *ofd)
{
const struct itimerspec its_null = {
.it_value = { 0, 0 },
.it_interval = { 0, 0 },
};
return timerfd_settime(ofd->fd, 0, &its_null, NULL);
}
/*! schedule the osmcoom-wrapped timerfd to occur first at \a first, then periodically at \a interval
* \param[in] ofd Osmocom wrapped timerfd
* \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
* \param[in] interval Time interval at which subsequent timer shall fire
* \returns 0 on success; negative on error */
int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
const struct timespec *interval)
{
struct itimerspec its;
if (ofd->fd < 0)
return -EINVAL;
/* first expiration */
if (first)
its.it_value = *first;
else
its.it_value = *interval;
/* repeating interval */
its.it_interval = *interval;
return timerfd_settime(ofd->fd, 0, &its, NULL);
}
/*! setup osmocom-wrapped timerfd
* \param[inout] ofd Osmocom-wrapped timerfd on which to operate
* \param[in] cb Call-back function called when timerfd becomes readable
* \param[in] data Opaque data to be passed on to call-back
* \returns 0 on success; negative on error
*
* We simply initialize the data structures here, but do not yet
* schedule the timer.
*/
int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
{
ofd->cb = cb;
ofd->data = data;
ofd->when = BSC_FD_READ;
if (ofd->fd < 0) {
ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (ofd->fd < 0)
return ofd->fd;
osmo_fd_register(ofd);
}
return 0;
}
#endif /* HAVE_SYS_TIMERFD_H */
/*! @} */
#endif /* _HAVE_SYS_SELECT_H */