Drop main() prototype. Syncs with NetBSD-8
[minix.git] / minix / tests / test51.c
blobde2712bda716dc011d0d1157e8b7e64ecde81589
1 /* Test51.c
3 * Test getcontext, setcontext, makecontext, and swapcontext system calls.
5 * Part of this test is somewhat based on the GNU GCC ucontext test set.
7 */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <ucontext.h>
15 #include <math.h>
16 #include <fenv.h>
18 #include <sys/signal.h>
20 /* We can't use a global subtest variable reliably, because threads might
21 change the value when we reenter a thread (i.e., report wrong subtest
22 number). */
23 #define err(st, en) do { subtest = st; e(en); } while(0)
25 void do_calcs(void);
26 void do_child(void);
27 void do_parent(void);
28 void func1(int a, int b, int c, int d, int e, int f, int g, int h, int
29 i, int j, int k, int l, int m, int n, int o, int p, int q, int r, int s,
30 int t, int u, int v, int w, int x, int y, int z, int aa, int bb);
31 void func2(void);
32 void just_exit(void);
33 void test_brk(void);
34 void verify_main_reenter(void);
36 int max_error = 5;
37 #include "common.h"
39 #define SSIZE 32768
40 #define ROUNDS 10
41 #define SWAPS 10
44 int subtest;
45 ucontext_t ctx[3];
46 int entered_func1, entered_func2, reentered_main, entered_overflow;
48 static char st_stack[SSIZE];
49 static volatile int shift, global;
51 void do_calcs(void)
53 float a, b, c, d, e;
54 float foo, bar;
55 int i;
57 a = 1.1;
58 b = 2.2;
59 c = 3.3;
60 d = 4.4;
61 e = 5.5;
63 foo = a * b; /* 2.42 */
64 bar = c * d; /* 14.52 */
66 i = 0;
67 while(i < ROUNDS) {
68 foo += c; /* 5.72 */
69 foo *= d; /* 25.168 */
70 foo /= e; /* 4.5760 */
71 bar -= a; /* 13.42 */
72 bar *= b; /* 29.524 */
73 bar /= e; /* 5.3680 */
75 /* Undo */
76 foo *= e;
77 foo /= d;
78 foo -= c;
80 bar *= e;
81 bar /= b;
82 bar += a;
84 i++;
86 if(fabs(foo - (a * b)) > 0.0001) err(8, 1);
87 if(fabs(bar - (c * d)) > 0.0001) err(8, 2);
90 void do_child(void)
92 int s;
93 s = 1;
95 /* Initialize FPU context and verify it's set to round to nearest. */
96 if (fegetround() != FE_TONEAREST) err(9, 1);
98 /* Now we change the rounding to something else, and this should be preserved
99 between context swaps. */
100 if (fesetround(FE_DOWNWARD) != 0) err(9, 2);
102 while(s < SWAPS) {
103 s++;
104 if (swapcontext(&ctx[2], &ctx[1]) == -1) err(9, 2);
105 do_calcs();
106 if (fegetround() != FE_DOWNWARD) err(9, 4);
108 quit();
111 void do_parent(void)
113 ucontext_t dummy;
114 int s;
115 s = 1;
117 /* Initialize FPU context and verify it's set to round to nearest. */
118 if (fegetround() != FE_TONEAREST) err(10, 1);
120 /* Now we change the rounding to something else, and this should be preserved
121 between context swaps. */
122 if (fesetround(FE_UPWARD) != 0) err(10, 2);
124 /* Quick check to make sure that getcontext does not reset the FPU state. */
125 getcontext(&dummy);
127 if (fegetround() != FE_UPWARD) err(10, 3);
129 while(s < SWAPS) {
130 do_calcs();
131 if (fegetround() != FE_UPWARD) err(10, 4);
132 s++;
133 if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 5);
135 /* Returning to main thread through uc_link */
138 static void fail(void)
140 /* Shouldn't get here */
141 err(5, 1);
144 void func1(int a, int b, int c, int d, int e, int f, int g,
145 int h, int i, int j, int k, int l, int m, int n,
146 int o, int p, int q, int r, int s, int t, int u,
147 int v, int w, int x, int y, int z, int aa, int bb)
149 if ( a != (0x0000001 << shift) || b != (0x0000004 << shift) ||
150 c != (0x0000010 << shift) || d != (0x0000040 << shift) ||
151 e != (0x0000100 << shift) || f != (0x0000400 << shift) ||
152 g != (0x0001000 << shift) || h != (0x0004000 << shift) ||
153 i != (0x0010000 << shift) || j != (0x0040000 << shift) ||
154 k != (0x0100000 << shift) || l != (0x0400000 << shift) ||
155 m != (0x1000000 << shift) || n != (0x4000000 << shift) ||
156 o != (0x0000002 << shift) || p != (0x0000008 << shift) ||
157 q != (0x0000020 << shift) || r != (0x0000080 << shift) ||
158 s != (0x0000200 << shift) || t != (0x0000800 << shift) ||
159 u != (0x0002000 << shift) || v != (0x0008000 << shift) ||
160 w != (0x0020000 << shift) || x != (0x0080000 << shift) ||
161 y != (0x0200000 << shift) || z != (0x0800000 << shift) ||
162 aa != (0x2000000 << shift) || bb != (0x8000000 << shift) ) {
163 err(2, 1);
166 if (shift && swapcontext (&ctx[1], &ctx[2]) != 0) err(2, 2);
167 shift++;
168 entered_func1++;
171 void func2(void)
173 if (swapcontext(&ctx[2], &ctx[1]) != 0) err(3, 1);
174 entered_func2++;
177 void just_exit(void)
179 if (errct == 0) printf("ok\n");
180 _exit(1);
183 void test_brk(void)
185 char *big_stack;
187 big_stack = malloc(16 * 1024 * 1024); /* 16 MB */
188 /* If this fails, it is likely brk system call failed due stack/data segments
189 collision detection. Unless the system really is low on memory, this is an
190 error. */
191 if (big_stack == NULL) err(7, 1);
194 void verify_main_reenter(void)
196 if (reentered_main == 0) err(4, 1);
199 int set_context_test_value;
200 static void set_context_test_thread1(void){
201 set_context_test_value |= 0x1;
202 setcontext(&ctx[2]);
203 err(1, 24);
207 static void set_context_test_thread2(void){
208 set_context_test_value |= 0x1 << 1;
209 setcontext(&ctx[0]);
210 err(1, 23);
213 int main(void)
215 start(51);
217 atexit(verify_main_reenter);
219 /* Save current context in ctx[0] */
220 if (getcontext(&ctx[0]) != 0) {
221 /* Don't verify reentering main, not going to happen */
222 atexit(just_exit);
223 err(1, 1);
226 ctx[1] = ctx[0];
227 ctx[1].uc_stack.ss_sp = st_stack;
228 ctx[1].uc_stack.ss_size = SSIZE;
229 ctx[1].uc_link = &ctx[0]; /* When done running, return here */
231 /* ctx[1] is going to run func1 and then return here (uc_link). */
232 /* We'll see later on whether makecontext worked. */
233 makecontext(&ctx[1], (void (*) (void)) func1, 28,
234 (0x0000001 << shift), (0x0000004 << shift),
235 (0x0000010 << shift), (0x0000040 << shift),
236 (0x0000100 << shift), (0x0000400 << shift),
237 (0x0001000 << shift), (0x0004000 << shift),
238 (0x0010000 << shift), (0x0040000 << shift),
239 (0x0100000 << shift), (0x0400000 << shift),
240 (0x1000000 << shift), (0x4000000 << shift),
241 (0x0000002 << shift), (0x0000008 << shift),
242 (0x0000020 << shift), (0x0000080 << shift),
243 (0x0000200 << shift), (0x0000800 << shift),
244 (0x0002000 << shift), (0x0008000 << shift),
245 (0x0020000 << shift), (0x0080000 << shift),
246 (0x0200000 << shift), (0x0800000 << shift),
247 (0x2000000 << shift), (0x8000000 << shift));
249 if (++global == 1) {
250 /* First time we're here. Let's run ctx[1] and return to ctx[0] when
251 * we're done. Note that we return to above the 'makecontext' call. */
252 if (setcontext(&ctx[1]) != 0) err(1, 2);
254 if (global != 2) {
255 /* When ++global was 1 we let ctx[1] run and returned to ctx[0], so
256 above ++global is executed again and should've become 2. */
257 err(1, 3);
260 /* Setup ctx[2] to run func2 */
261 if (getcontext(&ctx[2]) != 0) err(1, 4);
262 ctx[2].uc_stack.ss_sp = malloc(SSIZE);
263 ctx[2].uc_stack.ss_size = SSIZE;
264 ctx[2].uc_link = &ctx[1];
265 makecontext(&ctx[2], (void (*) (void)) func2, 0);
267 /* Now things become tricky. ctx[2] is set up such that when it finishes
268 running, and starts ctx[1] again. However, func1 swaps back to func2. Then,
269 when func2 has finished running, we continue with ctx[1] and, finally, we
270 return to ctx[0]. */
271 if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 5); /* makecontext failed? */
272 reentered_main = 1;
274 /* The call graph is as follows:
276 * ########
277 * /--------># main #
278 * 7 /----########----\
279 * | | ^ |
280 * | 1 2 3
281 * | V | V
282 * #########----/ #########
283 * # func1 #<-------4-------# func2 #
284 * #########--------5------>#########
285 * ^ |
286 * | |
287 * \---------6--------/
289 * Main calls func1, func1 increases entered_func1, and returns to main. Main
290 * calls func2, swaps to func1, swaps to func2, which increases entered_func2,
291 * continues with func1, which increases entered_func1 again, continues to
292 * main, where reentered_main is set to 1. In effect, entered_func1 == 2,
293 * entered_func2 == 1, reentered_main == 1. Verify that. */
295 if (entered_func1 != 2) err(1, 6);
296 if (entered_func2 != 1) err(1, 7);
297 /* reentered_main == 1 is verified upon exit */
299 /* Try to allocate too small a stack */
300 free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
301 if (getcontext(&ctx[2]) != 0) err(1, 8);
302 ctx[2].uc_stack.ss_sp = malloc(MINSIGSTKSZ-1);
303 ctx[2].uc_stack.ss_size = MINSIGSTKSZ-1;
304 ctx[2].uc_link = &ctx[0];
305 makecontext(&ctx[2], (void (*) (void)) fail, 0);
306 /* Because makecontext is void, we can only detect an error by trying to use
307 the invalid context */
308 if (swapcontext(&ctx[0], &ctx[2]) == 0) err(1, 9);
310 /* Try to allocate a huge stack to force the usage of brk/sbrk system call
311 to enlarge the data segment. Because we are fiddling with the stack
312 pointer, the OS might think the stack segment and data segment have
313 collided and kill us. This is wrong and therefore the following should
314 work. */
315 free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
316 if (getcontext(&ctx[2]) != 0) err(1, 14);
317 ctx[2].uc_stack.ss_sp = malloc(8 * 1024 * 1024); /* 8 MB */
318 ctx[2].uc_stack.ss_size = 8 * 1024 * 1024;
319 ctx[2].uc_link = &ctx[0];
320 makecontext(&ctx[2], (void (*) (void)) test_brk, 0);
321 if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 15);
323 ctx[1].uc_link = &ctx[0];
324 ctx[2].uc_link = NULL;
325 makecontext(&ctx[1], (void (*) (void)) do_parent, 0);
326 makecontext(&ctx[2], (void (*) (void)) do_child, 0);
327 if (swapcontext(&ctx[0], &ctx[2]) == -1) err(1, 16);
329 /* test setcontext first do some cleanup */
331 free(ctx[1].uc_stack.ss_sp);
332 free(ctx[2].uc_stack.ss_sp);
334 memset(&ctx[0],0,sizeof(ucontext_t));
335 memset(&ctx[1],0,sizeof(ucontext_t));
336 memset(&ctx[2],0,sizeof(ucontext_t));
339 /* create 3 new contexts */
340 volatile int cb =1;
342 /* control will be returned here */
343 set_context_test_value = 0;
344 if (getcontext(&ctx[0]) != 0) err(1, 17);
345 if (set_context_test_value != 0x3) err(1, 20);
346 if (cb == 1) {
347 cb =0;
348 if (getcontext(&ctx[1]) != 0) err(1, 18);
349 if (getcontext(&ctx[2]) != 0) err(1, 19);
351 // allocate new stacks */
352 ctx[1].uc_stack.ss_sp = malloc(SSIZE);
353 ctx[1].uc_stack.ss_size = SSIZE;
354 ctx[2].uc_stack.ss_sp = malloc(SSIZE);
355 ctx[2].uc_stack.ss_size = SSIZE;
357 makecontext(&ctx[1],set_context_test_thread1,0);
358 makecontext(&ctx[2],set_context_test_thread2,0);
359 if( setcontext(&ctx[1]) != 0){
360 err(1, 21);
362 err(1, 22);
365 quit();
366 return(-1);