Add qemu 2.4.0
[kvmfornfv.git] / qemu / tests / test-coroutine.c
1 /*
2  * Coroutine tests
3  *
4  * Copyright IBM, Corp. 2011
5  *
6  * Authors:
7  *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  *
12  */
13
14 #include <glib.h>
15 #include "block/coroutine.h"
16 #include "block/coroutine_int.h"
17
18 /*
19  * Check that qemu_in_coroutine() works
20  */
21
22 static void coroutine_fn verify_in_coroutine(void *opaque)
23 {
24     g_assert(qemu_in_coroutine());
25 }
26
27 static void test_in_coroutine(void)
28 {
29     Coroutine *coroutine;
30
31     g_assert(!qemu_in_coroutine());
32
33     coroutine = qemu_coroutine_create(verify_in_coroutine);
34     qemu_coroutine_enter(coroutine, NULL);
35 }
36
37 /*
38  * Check that qemu_coroutine_self() works
39  */
40
41 static void coroutine_fn verify_self(void *opaque)
42 {
43     g_assert(qemu_coroutine_self() == opaque);
44 }
45
46 static void test_self(void)
47 {
48     Coroutine *coroutine;
49
50     coroutine = qemu_coroutine_create(verify_self);
51     qemu_coroutine_enter(coroutine, coroutine);
52 }
53
54 /*
55  * Check that coroutines may nest multiple levels
56  */
57
58 typedef struct {
59     unsigned int n_enter;   /* num coroutines entered */
60     unsigned int n_return;  /* num coroutines returned */
61     unsigned int max;       /* maximum level of nesting */
62 } NestData;
63
64 static void coroutine_fn nest(void *opaque)
65 {
66     NestData *nd = opaque;
67
68     nd->n_enter++;
69
70     if (nd->n_enter < nd->max) {
71         Coroutine *child;
72
73         child = qemu_coroutine_create(nest);
74         qemu_coroutine_enter(child, nd);
75     }
76
77     nd->n_return++;
78 }
79
80 static void test_nesting(void)
81 {
82     Coroutine *root;
83     NestData nd = {
84         .n_enter  = 0,
85         .n_return = 0,
86         .max      = 128,
87     };
88
89     root = qemu_coroutine_create(nest);
90     qemu_coroutine_enter(root, &nd);
91
92     /* Must enter and return from max nesting level */
93     g_assert_cmpint(nd.n_enter, ==, nd.max);
94     g_assert_cmpint(nd.n_return, ==, nd.max);
95 }
96
97 /*
98  * Check that yield/enter transfer control correctly
99  */
100
101 static void coroutine_fn yield_5_times(void *opaque)
102 {
103     bool *done = opaque;
104     int i;
105
106     for (i = 0; i < 5; i++) {
107         qemu_coroutine_yield();
108     }
109     *done = true;
110 }
111
112 static void test_yield(void)
113 {
114     Coroutine *coroutine;
115     bool done = false;
116     int i = -1; /* one extra time to return from coroutine */
117
118     coroutine = qemu_coroutine_create(yield_5_times);
119     while (!done) {
120         qemu_coroutine_enter(coroutine, &done);
121         i++;
122     }
123     g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
124 }
125
126 static void coroutine_fn c2_fn(void *opaque)
127 {
128     qemu_coroutine_yield();
129 }
130
131 static void coroutine_fn c1_fn(void *opaque)
132 {
133     Coroutine *c2 = opaque;
134     qemu_coroutine_enter(c2, NULL);
135 }
136
137 static void test_co_queue(void)
138 {
139     Coroutine *c1;
140     Coroutine *c2;
141
142     c1 = qemu_coroutine_create(c1_fn);
143     c2 = qemu_coroutine_create(c2_fn);
144
145     qemu_coroutine_enter(c1, c2);
146     memset(c1, 0xff, sizeof(Coroutine));
147     qemu_coroutine_enter(c2, NULL);
148 }
149
150 /*
151  * Check that creation, enter, and return work
152  */
153
154 static void coroutine_fn set_and_exit(void *opaque)
155 {
156     bool *done = opaque;
157
158     *done = true;
159 }
160
161 static void test_lifecycle(void)
162 {
163     Coroutine *coroutine;
164     bool done = false;
165
166     /* Create, enter, and return from coroutine */
167     coroutine = qemu_coroutine_create(set_and_exit);
168     qemu_coroutine_enter(coroutine, &done);
169     g_assert(done); /* expect done to be true (first time) */
170
171     /* Repeat to check that no state affects this test */
172     done = false;
173     coroutine = qemu_coroutine_create(set_and_exit);
174     qemu_coroutine_enter(coroutine, &done);
175     g_assert(done); /* expect done to be true (second time) */
176 }
177
178
179 #define RECORD_SIZE 10 /* Leave some room for expansion */
180 struct coroutine_position {
181     int func;
182     int state;
183 };
184 static struct coroutine_position records[RECORD_SIZE];
185 static unsigned record_pos;
186
187 static void record_push(int func, int state)
188 {
189     struct coroutine_position *cp = &records[record_pos++];
190     g_assert_cmpint(record_pos, <, RECORD_SIZE);
191     cp->func = func;
192     cp->state = state;
193 }
194
195 static void coroutine_fn co_order_test(void *opaque)
196 {
197     record_push(2, 1);
198     g_assert(qemu_in_coroutine());
199     qemu_coroutine_yield();
200     record_push(2, 2);
201     g_assert(qemu_in_coroutine());
202 }
203
204 static void do_order_test(void)
205 {
206     Coroutine *co;
207
208     co = qemu_coroutine_create(co_order_test);
209     record_push(1, 1);
210     qemu_coroutine_enter(co, NULL);
211     record_push(1, 2);
212     g_assert(!qemu_in_coroutine());
213     qemu_coroutine_enter(co, NULL);
214     record_push(1, 3);
215     g_assert(!qemu_in_coroutine());
216 }
217
218 static void test_order(void)
219 {
220     int i;
221     const struct coroutine_position expected_pos[] = {
222         {1, 1,}, {2, 1}, {1, 2}, {2, 2}, {1, 3}
223     };
224     do_order_test();
225     g_assert_cmpint(record_pos, ==, 5);
226     for (i = 0; i < record_pos; i++) {
227         g_assert_cmpint(records[i].func , ==, expected_pos[i].func );
228         g_assert_cmpint(records[i].state, ==, expected_pos[i].state);
229     }
230 }
231 /*
232  * Lifecycle benchmark
233  */
234
235 static void coroutine_fn empty_coroutine(void *opaque)
236 {
237     /* Do nothing */
238 }
239
240 static void perf_lifecycle(void)
241 {
242     Coroutine *coroutine;
243     unsigned int i, max;
244     double duration;
245
246     max = 1000000;
247
248     g_test_timer_start();
249     for (i = 0; i < max; i++) {
250         coroutine = qemu_coroutine_create(empty_coroutine);
251         qemu_coroutine_enter(coroutine, NULL);
252     }
253     duration = g_test_timer_elapsed();
254
255     g_test_message("Lifecycle %u iterations: %f s\n", max, duration);
256 }
257
258 static void perf_nesting(void)
259 {
260     unsigned int i, maxcycles, maxnesting;
261     double duration;
262
263     maxcycles = 10000;
264     maxnesting = 1000;
265     Coroutine *root;
266
267     g_test_timer_start();
268     for (i = 0; i < maxcycles; i++) {
269         NestData nd = {
270             .n_enter  = 0,
271             .n_return = 0,
272             .max      = maxnesting,
273         };
274         root = qemu_coroutine_create(nest);
275         qemu_coroutine_enter(root, &nd);
276     }
277     duration = g_test_timer_elapsed();
278
279     g_test_message("Nesting %u iterations of %u depth each: %f s\n",
280         maxcycles, maxnesting, duration);
281 }
282
283 /*
284  * Yield benchmark
285  */
286
287 static void coroutine_fn yield_loop(void *opaque)
288 {
289     unsigned int *counter = opaque;
290
291     while ((*counter) > 0) {
292         (*counter)--;
293         qemu_coroutine_yield();
294     }
295 }
296
297 static void perf_yield(void)
298 {
299     unsigned int i, maxcycles;
300     double duration;
301
302     maxcycles = 100000000;
303     i = maxcycles;
304     Coroutine *coroutine = qemu_coroutine_create(yield_loop);
305
306     g_test_timer_start();
307     while (i > 0) {
308         qemu_coroutine_enter(coroutine, &i);
309     }
310     duration = g_test_timer_elapsed();
311
312     g_test_message("Yield %u iterations: %f s\n",
313         maxcycles, duration);
314 }
315
316 static __attribute__((noinline)) void dummy(unsigned *i)
317 {
318     (*i)--;
319 }
320
321 static void perf_baseline(void)
322 {
323     unsigned int i, maxcycles;
324     double duration;
325
326     maxcycles = 100000000;
327     i = maxcycles;
328
329     g_test_timer_start();
330     while (i > 0) {
331         dummy(&i);
332     }
333     duration = g_test_timer_elapsed();
334
335     g_test_message("Function call %u iterations: %f s\n",
336         maxcycles, duration);
337 }
338
339 static __attribute__((noinline)) void perf_cost_func(void *opaque)
340 {
341     qemu_coroutine_yield();
342 }
343
344 static void perf_cost(void)
345 {
346     const unsigned long maxcycles = 40000000;
347     unsigned long i = 0;
348     double duration;
349     unsigned long ops;
350     Coroutine *co;
351
352     g_test_timer_start();
353     while (i++ < maxcycles) {
354         co = qemu_coroutine_create(perf_cost_func);
355         qemu_coroutine_enter(co, &i);
356         qemu_coroutine_enter(co, NULL);
357     }
358     duration = g_test_timer_elapsed();
359     ops = (long)(maxcycles / (duration * 1000));
360
361     g_test_message("Run operation %lu iterations %f s, %luK operations/s, "
362                    "%luns per coroutine",
363                    maxcycles,
364                    duration, ops,
365                    (unsigned long)(1000000000.0 * duration / maxcycles));
366 }
367
368 int main(int argc, char **argv)
369 {
370     g_test_init(&argc, &argv, NULL);
371     g_test_add_func("/basic/co_queue", test_co_queue);
372     g_test_add_func("/basic/lifecycle", test_lifecycle);
373     g_test_add_func("/basic/yield", test_yield);
374     g_test_add_func("/basic/nesting", test_nesting);
375     g_test_add_func("/basic/self", test_self);
376     g_test_add_func("/basic/in_coroutine", test_in_coroutine);
377     g_test_add_func("/basic/order", test_order);
378     if (g_test_perf()) {
379         g_test_add_func("/perf/lifecycle", perf_lifecycle);
380         g_test_add_func("/perf/nesting", perf_nesting);
381         g_test_add_func("/perf/yield", perf_yield);
382         g_test_add_func("/perf/function-call", perf_baseline);
383         g_test_add_func("/perf/cost", perf_cost);
384     }
385     return g_test_run();
386 }