summaryrefslogtreecommitdiffstats
path: root/src/timer_clockgettime.c
blob: 8d9760c5d6d86ca62c659c91c2ae68beac5c4606 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * (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 <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);
}

/*! @} */