4 * Copyright IBM, Corp. 2011
7 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
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.
15 #include "block/coroutine.h"
18 * Check that qemu_in_coroutine() works
21 static void coroutine_fn
verify_in_coroutine(void *opaque
)
23 g_assert(qemu_in_coroutine());
26 static void test_in_coroutine(void)
30 g_assert(!qemu_in_coroutine());
32 coroutine
= qemu_coroutine_create(verify_in_coroutine
);
33 qemu_coroutine_enter(coroutine
, NULL
);
37 * Check that qemu_coroutine_self() works
40 static void coroutine_fn
verify_self(void *opaque
)
42 g_assert(qemu_coroutine_self() == opaque
);
45 static void test_self(void)
49 coroutine
= qemu_coroutine_create(verify_self
);
50 qemu_coroutine_enter(coroutine
, coroutine
);
54 * Check that coroutines may nest multiple levels
58 unsigned int n_enter
; /* num coroutines entered */
59 unsigned int n_return
; /* num coroutines returned */
60 unsigned int max
; /* maximum level of nesting */
63 static void coroutine_fn
nest(void *opaque
)
65 NestData
*nd
= opaque
;
69 if (nd
->n_enter
< nd
->max
) {
72 child
= qemu_coroutine_create(nest
);
73 qemu_coroutine_enter(child
, nd
);
79 static void test_nesting(void)
88 root
= qemu_coroutine_create(nest
);
89 qemu_coroutine_enter(root
, &nd
);
91 /* Must enter and return from max nesting level */
92 g_assert_cmpint(nd
.n_enter
, ==, nd
.max
);
93 g_assert_cmpint(nd
.n_return
, ==, nd
.max
);
97 * Check that yield/enter transfer control correctly
100 static void coroutine_fn
yield_5_times(void *opaque
)
105 for (i
= 0; i
< 5; i
++) {
106 qemu_coroutine_yield();
111 static void test_yield(void)
113 Coroutine
*coroutine
;
115 int i
= -1; /* one extra time to return from coroutine */
117 coroutine
= qemu_coroutine_create(yield_5_times
);
119 qemu_coroutine_enter(coroutine
, &done
);
122 g_assert_cmpint(i
, ==, 5); /* coroutine must yield 5 times */
126 * Check that creation, enter, and return work
129 static void coroutine_fn
set_and_exit(void *opaque
)
136 static void test_lifecycle(void)
138 Coroutine
*coroutine
;
141 /* Create, enter, and return from coroutine */
142 coroutine
= qemu_coroutine_create(set_and_exit
);
143 qemu_coroutine_enter(coroutine
, &done
);
144 g_assert(done
); /* expect done to be true (first time) */
146 /* Repeat to check that no state affects this test */
148 coroutine
= qemu_coroutine_create(set_and_exit
);
149 qemu_coroutine_enter(coroutine
, &done
);
150 g_assert(done
); /* expect done to be true (second time) */
154 #define RECORD_SIZE 10 /* Leave some room for expansion */
155 struct coroutine_position
{
159 static struct coroutine_position records
[RECORD_SIZE
];
160 static unsigned record_pos
;
162 static void record_push(int func
, int state
)
164 struct coroutine_position
*cp
= &records
[record_pos
++];
165 g_assert_cmpint(record_pos
, <, RECORD_SIZE
);
170 static void coroutine_fn
co_order_test(void *opaque
)
173 g_assert(qemu_in_coroutine());
174 qemu_coroutine_yield();
176 g_assert(qemu_in_coroutine());
179 static void do_order_test(void)
183 co
= qemu_coroutine_create(co_order_test
);
185 qemu_coroutine_enter(co
, NULL
);
187 g_assert(!qemu_in_coroutine());
188 qemu_coroutine_enter(co
, NULL
);
190 g_assert(!qemu_in_coroutine());
193 static void test_order(void)
196 const struct coroutine_position expected_pos
[] = {
197 {1, 1,}, {2, 1}, {1, 2}, {2, 2}, {1, 3}
200 g_assert_cmpint(record_pos
, ==, 5);
201 for (i
= 0; i
< record_pos
; i
++) {
202 g_assert_cmpint(records
[i
].func
, ==, expected_pos
[i
].func
);
203 g_assert_cmpint(records
[i
].state
, ==, expected_pos
[i
].state
);
207 * Lifecycle benchmark
210 static void coroutine_fn
empty_coroutine(void *opaque
)
215 static void perf_lifecycle(void)
217 Coroutine
*coroutine
;
223 g_test_timer_start();
224 for (i
= 0; i
< max
; i
++) {
225 coroutine
= qemu_coroutine_create(empty_coroutine
);
226 qemu_coroutine_enter(coroutine
, NULL
);
228 duration
= g_test_timer_elapsed();
230 g_test_message("Lifecycle %u iterations: %f s\n", max
, duration
);
233 static void perf_nesting(void)
235 unsigned int i
, maxcycles
, maxnesting
;
242 g_test_timer_start();
243 for (i
= 0; i
< maxcycles
; i
++) {
249 root
= qemu_coroutine_create(nest
);
250 qemu_coroutine_enter(root
, &nd
);
252 duration
= g_test_timer_elapsed();
254 g_test_message("Nesting %u iterations of %u depth each: %f s\n",
255 maxcycles
, maxnesting
, duration
);
262 static void coroutine_fn
yield_loop(void *opaque
)
264 unsigned int *counter
= opaque
;
266 while ((*counter
) > 0) {
268 qemu_coroutine_yield();
272 static void perf_yield(void)
274 unsigned int i
, maxcycles
;
277 maxcycles
= 100000000;
279 Coroutine
*coroutine
= qemu_coroutine_create(yield_loop
);
281 g_test_timer_start();
283 qemu_coroutine_enter(coroutine
, &i
);
285 duration
= g_test_timer_elapsed();
287 g_test_message("Yield %u iterations: %f s\n",
288 maxcycles
, duration
);
291 static __attribute__((noinline
)) void dummy(unsigned *i
)
296 static void perf_baseline(void)
298 unsigned int i
, maxcycles
;
301 maxcycles
= 100000000;
304 g_test_timer_start();
308 duration
= g_test_timer_elapsed();
310 g_test_message("Function call %u iterations: %f s\n",
311 maxcycles
, duration
);
314 static __attribute__((noinline
)) void perf_cost_func(void *opaque
)
316 qemu_coroutine_yield();
319 static void perf_cost(void)
321 const unsigned long maxcycles
= 40000000;
327 g_test_timer_start();
328 while (i
++ < maxcycles
) {
329 co
= qemu_coroutine_create(perf_cost_func
);
330 qemu_coroutine_enter(co
, &i
);
331 qemu_coroutine_enter(co
, NULL
);
333 duration
= g_test_timer_elapsed();
334 ops
= (long)(maxcycles
/ (duration
* 1000));
336 g_test_message("Run operation %lu iterations %f s, %luK operations/s, "
337 "%luns per coroutine",
340 (unsigned long)(1000000000 * duration
) / maxcycles
);
343 int main(int argc
, char **argv
)
345 g_test_init(&argc
, &argv
, NULL
);
346 g_test_add_func("/basic/lifecycle", test_lifecycle
);
347 g_test_add_func("/basic/yield", test_yield
);
348 g_test_add_func("/basic/nesting", test_nesting
);
349 g_test_add_func("/basic/self", test_self
);
350 g_test_add_func("/basic/in_coroutine", test_in_coroutine
);
351 g_test_add_func("/basic/order", test_order
);
353 g_test_add_func("/perf/lifecycle", perf_lifecycle
);
354 g_test_add_func("/perf/nesting", perf_nesting
);
355 g_test_add_func("/perf/yield", perf_yield
);
356 g_test_add_func("/perf/function-call", perf_baseline
);
357 g_test_add_func("/perf/cost", perf_cost
);