/* * (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> * All Rights Reserved * * Authors: Pau Espin Pedrol <pespin@sysmocom.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /*! \addtogroup timer * @{ * \file timer_clockgettime.c * Overriding Time: osmo_clock_gettime() * - Useful to write and reproduce tests that depend on specific time * factors. This API allows to fake the timespec provided by `clock_gettime()` * by using a small shim osmo_clock_gettime(). * - Choose the clock you want to override, for instance CLOCK_MONOTONIC. * - If the clock override is disabled (default) for a given clock, * osmo_clock_gettime() will do the same as regular `clock_gettime()`. * - If you want osmo_clock_gettime() to provide a specific time, you must * enable time override with osmo_clock_override_enable(), * then set a pointer to the timespec storing the fake time for that * specific clock (`struct timespec *ts = * osmo_clock_override_gettimespec()`) and set it as * desired. Next time osmo_clock_gettime() is called, it will return the * values previously set through the ts pointer. * - A helper osmo_clock_override_add() is provided to increment a given * overriden clock with a specific amount of time. */ /*! \file timer_clockgettime.c */ #include "config.h" #ifdef HAVE_CLOCK_GETTIME #include <stdlib.h> #include <stdbool.h> #include <sys/time.h> #include <time.h> #include <osmocom/core/timer_compat.h> /*! An internal structure to handle overriden time for each clock type. */ struct fakeclock { bool override; struct timespec time; }; static struct fakeclock realtime; static struct fakeclock realtime_coarse; static struct fakeclock mono; static struct fakeclock mono_coarse; static struct fakeclock mono_raw; static struct fakeclock boottime; static struct fakeclock boottime; static struct fakeclock proc_cputime_id; static struct fakeclock th_cputime_id; static struct fakeclock* clkid_to_fakeclock(clockid_t clk_id) { switch(clk_id) { case CLOCK_REALTIME: return &realtime; case CLOCK_REALTIME_COARSE: return &realtime_coarse; case CLOCK_MONOTONIC: return &mono; case CLOCK_MONOTONIC_COARSE: return &mono_coarse; case CLOCK_MONOTONIC_RAW: return &mono_raw; case CLOCK_BOOTTIME: return &boottime; case CLOCK_PROCESS_CPUTIME_ID: return &proc_cputime_id; case CLOCK_THREAD_CPUTIME_ID: return &th_cputime_id; default: return NULL; } } /*! Shim around clock_gettime to be able to set the time manually. * * To override, use osmo_clock_override_enable and set the desired * current time with osmo_clock_gettimespec. */ int osmo_clock_gettime(clockid_t clk_id, struct timespec *tp) { struct fakeclock* c = clkid_to_fakeclock(clk_id); if (!c || !c->override) return clock_gettime(clk_id, tp); *tp = c->time; return 0; } /*! Convenience function to enable or disable a specific clock fake time. */ void osmo_clock_override_enable(clockid_t clk_id, bool enable) { struct fakeclock* c = clkid_to_fakeclock(clk_id); if (c) c->override = enable; } /*! Convenience function to return a pointer to the timespec handling the * fake time for clock clk_id. */ struct timespec *osmo_clock_override_gettimespec(clockid_t clk_id) { struct fakeclock* c = clkid_to_fakeclock(clk_id); if (c) return &c->time; return NULL; } /*! Convenience function to advance the fake time. * * Adds the given values to the clock time. */ void osmo_clock_override_add(clockid_t clk_id, time_t secs, long nsecs) { struct timespec val = { secs, nsecs }; struct fakeclock* c = clkid_to_fakeclock(clk_id); if (c) timespecadd(&c->time, &val, &c->time); } #endif /* HAVE_CLOCK_GETTIME */ /*! @} */