OpenVPN
test_schedule.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28#include "schedule.h"
29#include "test_common.h"
30
31static inline bool
32tv_lt(const struct timeval *t1, const struct timeval *t2)
33{
34 if (t1->tv_sec < t2->tv_sec)
35 {
36 return true;
37 }
38 else if (t1->tv_sec > t2->tv_sec)
39 {
40 return false;
41 }
42 else
43 {
44 return t1->tv_usec < t2->tv_usec;
45 }
46}
47
48static inline bool
49tv_le(const struct timeval *t1, const struct timeval *t2)
50{
51 if (t1->tv_sec < t2->tv_sec)
52 {
53 return true;
54 }
55 else if (t1->tv_sec > t2->tv_sec)
56 {
57 return false;
58 }
59 else
60 {
61 return t1->tv_usec <= t2->tv_usec;
62 }
63}
64
65static inline bool
66tv_eq(const struct timeval *t1, const struct timeval *t2)
67{
68 return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec;
69}
70
71static inline struct schedule_entry *
76
77/*
78 * Recursively check that the treap (btree) is
79 * internally consistent.
80 */
81unsigned int
82schedule_debug_entry(const struct schedule_entry *e, unsigned int depth,
83 unsigned int *count, struct timeval *least,
84 const struct timeval *min, const struct timeval *max)
85{
86 struct gc_arena gc = gc_new();
87 unsigned int maxdepth = depth;
88 if (e)
89 {
90 unsigned int d;
91
92 assert_ptr_not_equal(e, e->lt);
93 assert_ptr_not_equal(e, e->gt);
94 assert_ptr_not_equal(e, e->parent);
95 assert_true(!e->parent || e->parent != e->lt);
96 assert_true(!e->parent || e->parent != e->gt);
97 assert_true(!e->lt || e->lt != e->gt);
98
99 if (e->lt)
100 {
101 assert_ptr_equal(e->lt->parent, e);
102 assert_int_equal(schedule_entry_compare(e->lt, e), -1);
103 assert_true(e->lt->pri >= e->pri);
104 }
105
106 if (e->gt)
107 {
108 assert_ptr_equal(e->gt->parent, e);
109 assert_int_equal(schedule_entry_compare(e->gt, e), 1);
110 assert_true(e->gt->pri >= e->pri);
111 }
112
113 assert_true(tv_le(min, &e->tv));
114 assert_true(tv_le(&e->tv, max));
115
116 if (count)
117 {
118 ++(*count);
119 }
120
121 if (least && tv_lt(&e->tv, least))
122 {
123 *least = e->tv;
124 }
125
126 d = schedule_debug_entry(e->lt, depth + 1, count, least, min, &e->tv);
127 if (d > maxdepth)
128 {
129 maxdepth = d;
130 }
131
132 d = schedule_debug_entry(e->gt, depth + 1, count, least, &e->tv, max);
133 if (d > maxdepth)
134 {
135 maxdepth = d;
136 }
137 }
138 gc_free(&gc);
139 return maxdepth;
140}
141
142unsigned int
143schedule_debug(struct schedule *s, unsigned int *count, struct timeval *least)
144{
145 struct timeval min;
146 struct timeval max;
147
148 min.tv_sec = 0;
149 min.tv_usec = 0;
150 max.tv_sec = 0x7FFFFFFF;
151 max.tv_usec = 0x7FFFFFFF;
152
153 if (s->root)
154 {
155 assert_null(s->root->parent);
156 }
157 return schedule_debug_entry(s->root, 0, count, least, &min, &max);
158}
159
160void
161tv_randomize(struct timeval *tv)
162{
163 tv->tv_sec += random() % 100;
164 tv->tv_usec = random() % 100;
165}
166
167void
168schedule_verify(struct schedule *s, unsigned int n)
169{
170 struct gc_arena gc = gc_new();
171 struct timeval least;
172
173 least.tv_sec = least.tv_usec = 0x7FFFFFFF;
174
175 unsigned int count = 0;
176 unsigned int maxlev = schedule_debug(s, &count, &least);
177
178 /* a stupid algorithm to do C23 stdc_bit_ceil_ui/stdc_bit_width
179 * calculate roundup(log2 n) */
180 unsigned int bit_ceil_n = 1;
181 unsigned int log2n = 0;
182 while (bit_ceil_n < n)
183 {
184 bit_ceil_n <<= 1;
185 log2n++;
186 }
187
188 /* Since this is a binary tree the maximum level needs to be at least
189 * log2(n) */
190 assert_true(maxlev >= log2n);
192
193 if (e)
194 {
195 assert_true(tv_eq(&least, &e->tv));
196 }
197
198 gc_free(&gc);
199}
200
201void
202schedule_randomize_array(struct schedule_entry **array, unsigned int size)
203{
204 for (unsigned int i = 0; i < size; ++i)
205 {
206 const unsigned int src = (unsigned int)rand() % size;
207 struct schedule_entry *tmp = array[i];
208 if (i != src)
209 {
210 array[i] = array[src];
211 array[src] = tmp;
212 }
213 }
214}
215
216void
217schedule_print_work(struct schedule_entry *e, unsigned int indent)
218{
219 struct gc_arena gc = gc_new();
220 for (unsigned int i = 0; i < indent; ++i)
221 {
222 printf(" ");
223 }
224 if (e)
225 {
226 printf("%s [%u] e=" ptr_format ", p=" ptr_format " lt=" ptr_format " gt=" ptr_format "\n",
227 tv_string(&e->tv, &gc), e->pri, (ptr_type)e, (ptr_type)e->parent, (ptr_type)e->lt,
228 (ptr_type)e->gt);
229 schedule_print_work(e->lt, indent + 1);
230 schedule_print_work(e->gt, indent + 1);
231 }
232 else
233 {
234 printf("NULL\n");
235 }
236 gc_free(&gc);
237}
238
239void
241{
242 printf("*************************\n");
244}
245
246void
247schedule_test(void **state)
248{
249 struct gc_arena gc = gc_new();
250 unsigned int n = 1000;
251 unsigned int n_mod = 25;
252
253 struct schedule_entry **array;
254 struct schedule *s = schedule_init();
255 struct schedule_entry *e;
256
257 ALLOC_ARRAY(array, struct schedule_entry *, n);
258
259 for (unsigned int i = 0; i < n; ++i)
260 {
261 ALLOC_OBJ_CLEAR(array[i], struct schedule_entry);
262 tv_randomize(&array[i]->tv);
263 /*schedule_print (s);*/
264 /*schedule_verify (s, n);*/
265 schedule_add_modify(s, array[i]);
266 }
267
268 schedule_randomize_array(array, n);
269
270 /*schedule_print (s);*/
271 schedule_verify(s, n);
272
273 for (unsigned int j = 1; j <= n_mod; ++j)
274 {
275 /*printf("Modification Phase Pass %d\n", j);*/
276
277 for (unsigned int i = 0; i < n; ++i)
278 {
280 /*printf ("BEFORE %s\n", tv_string (&e->tv, &gc));*/
281 tv_randomize(&e->tv);
282 /*printf ("AFTER %s\n", tv_string (&e->tv, &gc));*/
284 /*schedule_verify (s, n);*/
285 /*schedule_print (s);*/
286 }
287 schedule_verify(s, n);
288 /*schedule_print (s);*/
289 }
290
291 /*printf ("INS=%d\n", z.ins);*/
292
293 while ((e = schedule_find_earliest_wakeup(s)))
294 {
296 /*schedule_verify (s, n);*/
297 }
298 schedule_verify(s, 0);
299 assert_null(s->root);
300
301 for (unsigned int i = 0; i < n; ++i)
302 {
303 free(array[i]);
304 }
305 free(array);
306 schedule_free(s);
307 gc_free(&gc);
308}
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1974
#define ALLOC_OBJ_CLEAR(dptr, type)
Allocate and zero-initialise memory for a single object of the given type.
Definition buffer.h:2036
#define ALLOC_ARRAY(dptr, type, n)
Allocate memory for an array of n elements of the given type.
Definition buffer.h:2052
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1958
unsigned long ptr_type
Definition common.h:59
#define ptr_format
Definition common.h:50
const char * tv_string(const struct timeval *tv, struct gc_arena *gc)
Definition otime.c:83
struct schedule * schedule_init(void)
Definition schedule.c:373
void schedule_remove_node(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:220
struct schedule_entry * schedule_find_least(struct schedule_entry *e)
Definition schedule.c:348
int schedule_entry_compare(const struct schedule_entry *e1, const struct schedule_entry *e2)
This method compares two schedule entries and return which one is earlier,later or equal.
Definition schedule.c:65
void schedule_add_modify(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:309
void schedule_free(struct schedule *s)
Definition schedule.c:382
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
Definition schedule.h:41
unsigned int pri
Definition schedule.h:43
struct timeval tv
Definition schedule.h:42
struct schedule_entry * lt
Definition schedule.h:45
struct schedule_entry * parent
Definition schedule.h:44
struct schedule_entry * gt
Definition schedule.h:46
struct schedule_entry * root
Definition schedule.h:52
#define random
Definition syshead.h:43
void schedule_verify(struct schedule *s, unsigned int n)
unsigned int schedule_debug(struct schedule *s, unsigned int *count, struct timeval *least)
static bool tv_le(const struct timeval *t1, const struct timeval *t2)
static bool tv_eq(const struct timeval *t1, const struct timeval *t2)
void tv_randomize(struct timeval *tv)
static struct schedule_entry * schedule_find_earliest_wakeup(struct schedule *s)
void schedule_randomize_array(struct schedule_entry **array, unsigned int size)
static bool tv_lt(const struct timeval *t1, const struct timeval *t2)
void schedule_test(void **state)
Runs the schedule test.
void schedule_print_work(struct schedule_entry *e, unsigned int indent)
unsigned int schedule_debug_entry(const struct schedule_entry *e, unsigned int depth, unsigned int *count, struct timeval *least, const struct timeval *min, const struct timeval *max)
void schedule_print(struct schedule *s)
struct gc_arena gc
Definition test_ssl.c:122