Commit | Line | Data |
---|---|---|
31d0247c MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Timeout extension for test vector framework | |
4 | * | |
5 | * (c) 2024 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the mLib utilities library. | |
11 | * | |
12 | * mLib is free software: you can redistribute it and/or modify it under | |
13 | * the terms of the GNU Library General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or (at | |
15 | * your option) any later version. | |
16 | * | |
17 | * mLib is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | |
20 | * License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU Library General Public | |
23 | * License along with mLib. If not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
25 | * USA. | |
26 | */ | |
27 | ||
28 | /*----- Header files ------------------------------------------------------*/ | |
29 | ||
30 | #include <ctype.h> | |
31 | #include <errno.h> | |
32 | #include <string.h> | |
33 | ||
34 | #include <sys/time.h> | |
35 | #include <sys/types.h> | |
36 | #include <unistd.h> | |
37 | ||
38 | #include "tvec.h" | |
b1a20bee MW |
39 | #include "tvec-timeout.h" |
40 | #include "tvec-types.h" | |
31d0247c MW |
41 | |
42 | /*----- Main code ---------------------------------------------------------*/ | |
43 | ||
44 | static void reset(struct tvec_timeoutctx *tc) | |
45 | { | |
46 | const struct tvec_timeoutenv *te = tc->te; | |
47 | ||
48 | tc->timer = te->timer; tc->t = te->t; tc->f &= ~TVTF_SETMASK; | |
49 | } | |
50 | ||
51 | /* --- @tvec_timeoutsetup@ --- * | |
52 | * | |
53 | * Arguments: @struct tvec_state *tv@ = test vector state | |
54 | * @const struct tvec_env *env@ = environment description | |
55 | * @void *pctx@ = parent context (ignored) | |
56 | * @void *ctx@ = context pointer to initialize | |
57 | * | |
58 | * Returns: --- | |
59 | * | |
60 | * Use: Initialize a timeout environment context. | |
61 | * | |
62 | * The environment description should be a @struct | |
63 | * tvec_timeoutenv@. | |
64 | */ | |
65 | ||
66 | void tvec_timeoutsetup(struct tvec_state *tv, const struct tvec_env *env, | |
67 | void *pctx, void *ctx) | |
68 | { | |
69 | struct tvec_timeoutctx *tc = ctx; | |
70 | const struct tvec_timeoutenv *te = (struct tvec_timeoutenv *)env; | |
71 | const struct tvec_env *subenv = te->env; | |
72 | ||
73 | tc->te = te; | |
74 | ||
75 | reset(tc); | |
76 | ||
b1a20bee MW |
77 | if (subenv && subenv->ctxsz) tc->subctx = x_alloc(tv->a, subenv->ctxsz); |
78 | else tc->subctx = 0; | |
31d0247c MW |
79 | if (subenv && subenv->setup) subenv->setup(tv, subenv, tc, tc->subctx); |
80 | } | |
81 | ||
814e42ff | 82 | /* --- @tvec_timeoutfindvar@, @setvar@ --- * |
31d0247c MW |
83 | * |
84 | * Arguments: @struct tvec_state *tv@ = test vector state | |
85 | * @const char *var@ = variable name to set | |
814e42ff MW |
86 | * @const union tvec_regval *rv@ = register value |
87 | * @void **ctx_out@ = where to put the @setvar@ context | |
31d0247c MW |
88 | * @void *ctx@ = context pointer |
89 | * | |
814e42ff MW |
90 | * Returns: @tvec_timeoutfindvar@ returns a pointer to the variable |
91 | * definition, or null; @setvar@ returns zero on success or | |
92 | * %$-1$% on error. | |
31d0247c | 93 | * |
814e42ff MW |
94 | * Use: Find a definition for a special variable. The following |
95 | * special variables are supported. | |
31d0247c | 96 | * |
814e42ff MW |
97 | * * %|@timeout|% is the duration to wait before killing the |
98 | * process. | |
99 | * | |
100 | * * %|@timer|% is the timer to use to measure the duration. | |
31d0247c MW |
101 | * |
102 | * Unrecognized variables are passed to the subordinate | |
103 | * environment, if there is one. | |
104 | */ | |
105 | ||
814e42ff MW |
106 | static int setvar(struct tvec_state *tv, const char *var, |
107 | const union tvec_regval *rv, void *ctx) | |
31d0247c MW |
108 | { |
109 | struct tvec_timeoutctx *tc = ctx; | |
31d0247c MW |
110 | |
111 | if (STRCMP(var, ==, "@timeout")) { | |
6e683a79 | 112 | if (tc->f&TVTF_SETTMO) return (tvec_dupregerr(tv, var)); |
814e42ff MW |
113 | tc->t = rv->f; tc->f |= TVTF_SETTMO; |
114 | } else if (STRCMP(var, ==, "@timer")) { | |
6e683a79 | 115 | if (tc->f&TVTF_SETTMR) return (tvec_dupregerr(tv, var)); |
814e42ff MW |
116 | tc->timer = rv->i; tc->f |= TVTF_SETTMR; |
117 | } else assert(!"unknown var"); | |
118 | return (0); | |
119 | } | |
31d0247c | 120 | |
814e42ff MW |
121 | static const struct tvec_vardef timeout_var = |
122 | { sizeof(struct tvec_reg), setvar, | |
d056fbdf | 123 | { "@timeout", &tvty_duration, -1, 0 } }; |
814e42ff MW |
124 | |
125 | static const struct tvec_iassoc timer_assocs[] = { | |
126 | { "REAL", ITIMER_REAL }, | |
127 | { "VIRTUAL", ITIMER_VIRTUAL }, | |
128 | { "PROF", ITIMER_PROF }, | |
129 | TVEC_ENDENUM | |
130 | }; | |
131 | static const struct tvec_ienuminfo timer_enum = | |
132 | { "interval-timer", timer_assocs, &tvrange_int }; | |
133 | static const struct tvec_vardef timer_var = | |
134 | { sizeof(struct tvec_reg), setvar, | |
d056fbdf | 135 | { "@timer", &tvty_ienum, -1, 0, { &timer_enum } } }; |
814e42ff MW |
136 | |
137 | const struct tvec_vardef *tvec_timeoutfindvar | |
138 | (struct tvec_state *tv, const char *var, void **ctx_out, void *ctx) | |
139 | { | |
140 | struct tvec_timeoutctx *tc = ctx; | |
141 | const struct tvec_timeoutenv *te = tc->te; | |
142 | const struct tvec_env *subenv = te->env; | |
31d0247c | 143 | |
814e42ff MW |
144 | if (STRCMP(var, ==, "@timeout")) { *ctx_out = tc; return (&timeout_var); } |
145 | else if (STRCMP(var, ==, "@timer")) { *ctx_out = tc; return (&timer_var); } | |
146 | else if (subenv && subenv->findvar) | |
147 | return (subenv->findvar(tv, var, ctx_out, tc->subctx)); | |
148 | else return (0); | |
31d0247c MW |
149 | } |
150 | ||
151 | /* --- @tvec_timeoutbefore@ --- * | |
152 | * | |
153 | * Arguments: @struct tvec_state *tv@ = test vector state | |
154 | * @void *ctx@ = context pointer | |
155 | * | |
156 | * Returns: --- | |
157 | * | |
158 | * Use: Invoke the subordinate environment's @before@ function to | |
159 | * prepare for the test. | |
160 | */ | |
161 | ||
162 | void tvec_timeoutbefore(struct tvec_state *tv, void *ctx) | |
163 | { | |
164 | struct tvec_timeoutctx *tc = ctx; | |
165 | const struct tvec_timeoutenv *te = tc->te; | |
166 | const struct tvec_env *subenv = te->env; | |
167 | ||
31d0247c MW |
168 | if (subenv && subenv->before) subenv->before(tv, tc->subctx); |
169 | } | |
170 | ||
c81c35df | 171 | /* --- @tvec_timeoutrun@ --- * |
31d0247c MW |
172 | * |
173 | * Arguments: @struct tvec_state *tv@ = test vector state | |
c81c35df MW |
174 | * @tvec_testfn *fn@ = test function to run |
175 | * @void *ctx@ = context pointer for the test function | |
31d0247c MW |
176 | * |
177 | * Returns: --- | |
178 | * | |
c81c35df | 179 | * Use: Runs a test with a timeout attached. |
31d0247c MW |
180 | */ |
181 | ||
c81c35df | 182 | void tvec_timeoutrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx) |
31d0247c MW |
183 | { |
184 | struct tvec_timeoutctx *tc = ctx; | |
185 | const struct tvec_timeoutenv *te = tc->te; | |
186 | const struct tvec_env *subenv = te->env; | |
c81c35df | 187 | struct itimerval itv; |
31d0247c | 188 | |
c81c35df MW |
189 | itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; |
190 | itv.it_value.tv_sec = tc->t; | |
191 | itv.it_value.tv_usec = (tc->t - itv.it_value.tv_sec)*1e6; | |
31d0247c | 192 | |
c81c35df MW |
193 | if (setitimer(tc->timer, &itv, 0)) |
194 | tvec_skip(tv, "failed to set interval timer: %s", strerror(errno)); | |
195 | else { | |
196 | if (subenv && subenv->run) subenv->run(tv, fn, tc->subctx); | |
c4ccbbf9 | 197 | else { fn(tv->in, tv->out, tc->subctx); tvec_check(tv, 0); } |
c81c35df MW |
198 | |
199 | itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; | |
200 | itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0; | |
201 | if (setitimer(tc->timer, &itv, 0)) | |
202 | tvec_error(tv, "failed to disarm interval timer: %s", strerror(errno)); | |
203 | } | |
31d0247c MW |
204 | } |
205 | ||
c81c35df | 206 | /* --- @tvec_timeoutafter@ --- * |
31d0247c MW |
207 | * |
208 | * Arguments: @struct tvec_state *tv@ = test vector state | |
209 | * @void *ctx@ = context pointer | |
210 | * | |
211 | * Returns: --- | |
212 | * | |
c81c35df MW |
213 | * Use: Invoke the subordinate environment's @after@ function to |
214 | * clean up after the test. | |
31d0247c MW |
215 | */ |
216 | ||
c81c35df | 217 | void tvec_timeoutafter(struct tvec_state *tv, void *ctx) |
31d0247c MW |
218 | { |
219 | struct tvec_timeoutctx *tc = ctx; | |
220 | const struct tvec_timeoutenv *te = tc->te; | |
221 | const struct tvec_env *subenv = te->env; | |
222 | ||
c81c35df MW |
223 | /* Reset variables. */ |
224 | reset(tc); | |
225 | ||
226 | /* Pass the call on to the subsidiary environment. */ | |
227 | if (subenv && subenv->after) subenv->after(tv, tc->subctx); | |
31d0247c MW |
228 | } |
229 | ||
c81c35df | 230 | /* --- @tvec_timeoutteardown@ --- * |
31d0247c MW |
231 | * |
232 | * Arguments: @struct tvec_state *tv@ = test vector state | |
c81c35df | 233 | * @void *ctx@ = context pointer |
31d0247c MW |
234 | * |
235 | * Returns: --- | |
236 | * | |
c81c35df | 237 | * Use: Tear down the timeoutmark environment. |
31d0247c MW |
238 | */ |
239 | ||
c81c35df | 240 | void tvec_timeoutteardown(struct tvec_state *tv, void *ctx) |
31d0247c MW |
241 | { |
242 | struct tvec_timeoutctx *tc = ctx; | |
243 | const struct tvec_timeoutenv *te = tc->te; | |
244 | const struct tvec_env *subenv = te->env; | |
31d0247c | 245 | |
c81c35df MW |
246 | /* Just call the subsidiary environment. */ |
247 | if (subenv && subenv->teardown) subenv->teardown(tv, tc->subctx); | |
b1a20bee | 248 | x_free(tv->a, tc->subctx); |
31d0247c MW |
249 | } |
250 | ||
251 | /*----- That's all, folks -------------------------------------------------*/ |