root / tests / test-coroutine.c @ 737e150e
History | View | Annotate | Download (4.7 kB)
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 |
|
17 |
/*
|
18 |
* Check that qemu_in_coroutine() works
|
19 |
*/
|
20 |
|
21 |
static void coroutine_fn verify_in_coroutine(void *opaque) |
22 |
{ |
23 |
g_assert(qemu_in_coroutine()); |
24 |
} |
25 |
|
26 |
static void test_in_coroutine(void) |
27 |
{ |
28 |
Coroutine *coroutine; |
29 |
|
30 |
g_assert(!qemu_in_coroutine()); |
31 |
|
32 |
coroutine = qemu_coroutine_create(verify_in_coroutine); |
33 |
qemu_coroutine_enter(coroutine, NULL);
|
34 |
} |
35 |
|
36 |
/*
|
37 |
* Check that qemu_coroutine_self() works
|
38 |
*/
|
39 |
|
40 |
static void coroutine_fn verify_self(void *opaque) |
41 |
{ |
42 |
g_assert(qemu_coroutine_self() == opaque); |
43 |
} |
44 |
|
45 |
static void test_self(void) |
46 |
{ |
47 |
Coroutine *coroutine; |
48 |
|
49 |
coroutine = qemu_coroutine_create(verify_self); |
50 |
qemu_coroutine_enter(coroutine, coroutine); |
51 |
} |
52 |
|
53 |
/*
|
54 |
* Check that coroutines may nest multiple levels
|
55 |
*/
|
56 |
|
57 |
typedef struct { |
58 |
unsigned int n_enter; /* num coroutines entered */ |
59 |
unsigned int n_return; /* num coroutines returned */ |
60 |
unsigned int max; /* maximum level of nesting */ |
61 |
} NestData; |
62 |
|
63 |
static void coroutine_fn nest(void *opaque) |
64 |
{ |
65 |
NestData *nd = opaque; |
66 |
|
67 |
nd->n_enter++; |
68 |
|
69 |
if (nd->n_enter < nd->max) {
|
70 |
Coroutine *child; |
71 |
|
72 |
child = qemu_coroutine_create(nest); |
73 |
qemu_coroutine_enter(child, nd); |
74 |
} |
75 |
|
76 |
nd->n_return++; |
77 |
} |
78 |
|
79 |
static void test_nesting(void) |
80 |
{ |
81 |
Coroutine *root; |
82 |
NestData nd = { |
83 |
.n_enter = 0,
|
84 |
.n_return = 0,
|
85 |
.max = 128,
|
86 |
}; |
87 |
|
88 |
root = qemu_coroutine_create(nest); |
89 |
qemu_coroutine_enter(root, &nd); |
90 |
|
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); |
94 |
} |
95 |
|
96 |
/*
|
97 |
* Check that yield/enter transfer control correctly
|
98 |
*/
|
99 |
|
100 |
static void coroutine_fn yield_5_times(void *opaque) |
101 |
{ |
102 |
bool *done = opaque;
|
103 |
int i;
|
104 |
|
105 |
for (i = 0; i < 5; i++) { |
106 |
qemu_coroutine_yield(); |
107 |
} |
108 |
*done = true;
|
109 |
} |
110 |
|
111 |
static void test_yield(void) |
112 |
{ |
113 |
Coroutine *coroutine; |
114 |
bool done = false; |
115 |
int i = -1; /* one extra time to return from coroutine */ |
116 |
|
117 |
coroutine = qemu_coroutine_create(yield_5_times); |
118 |
while (!done) {
|
119 |
qemu_coroutine_enter(coroutine, &done); |
120 |
i++; |
121 |
} |
122 |
g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ |
123 |
} |
124 |
|
125 |
/*
|
126 |
* Check that creation, enter, and return work
|
127 |
*/
|
128 |
|
129 |
static void coroutine_fn set_and_exit(void *opaque) |
130 |
{ |
131 |
bool *done = opaque;
|
132 |
|
133 |
*done = true;
|
134 |
} |
135 |
|
136 |
static void test_lifecycle(void) |
137 |
{ |
138 |
Coroutine *coroutine; |
139 |
bool done = false; |
140 |
|
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) */
|
145 |
|
146 |
/* Repeat to check that no state affects this test */
|
147 |
done = false;
|
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) */
|
151 |
} |
152 |
|
153 |
/*
|
154 |
* Lifecycle benchmark
|
155 |
*/
|
156 |
|
157 |
static void coroutine_fn empty_coroutine(void *opaque) |
158 |
{ |
159 |
/* Do nothing */
|
160 |
} |
161 |
|
162 |
static void perf_lifecycle(void) |
163 |
{ |
164 |
Coroutine *coroutine; |
165 |
unsigned int i, max; |
166 |
double duration;
|
167 |
|
168 |
max = 1000000;
|
169 |
|
170 |
g_test_timer_start(); |
171 |
for (i = 0; i < max; i++) { |
172 |
coroutine = qemu_coroutine_create(empty_coroutine); |
173 |
qemu_coroutine_enter(coroutine, NULL);
|
174 |
} |
175 |
duration = g_test_timer_elapsed(); |
176 |
|
177 |
g_test_message("Lifecycle %u iterations: %f s\n", max, duration);
|
178 |
} |
179 |
|
180 |
static void perf_nesting(void) |
181 |
{ |
182 |
unsigned int i, maxcycles, maxnesting; |
183 |
double duration;
|
184 |
|
185 |
maxcycles = 100000000;
|
186 |
maxnesting = 20000;
|
187 |
Coroutine *root; |
188 |
NestData nd = { |
189 |
.n_enter = 0,
|
190 |
.n_return = 0,
|
191 |
.max = maxnesting, |
192 |
}; |
193 |
|
194 |
g_test_timer_start(); |
195 |
for (i = 0; i < maxcycles; i++) { |
196 |
root = qemu_coroutine_create(nest); |
197 |
qemu_coroutine_enter(root, &nd); |
198 |
} |
199 |
duration = g_test_timer_elapsed(); |
200 |
|
201 |
g_test_message("Nesting %u iterations of %u depth each: %f s\n",
|
202 |
maxcycles, maxnesting, duration); |
203 |
} |
204 |
|
205 |
|
206 |
int main(int argc, char **argv) |
207 |
{ |
208 |
g_test_init(&argc, &argv, NULL);
|
209 |
g_test_add_func("/basic/lifecycle", test_lifecycle);
|
210 |
g_test_add_func("/basic/yield", test_yield);
|
211 |
g_test_add_func("/basic/nesting", test_nesting);
|
212 |
g_test_add_func("/basic/self", test_self);
|
213 |
g_test_add_func("/basic/in_coroutine", test_in_coroutine);
|
214 |
if (g_test_perf()) {
|
215 |
g_test_add_func("/perf/lifecycle", perf_lifecycle);
|
216 |
g_test_add_func("/perf/nesting", perf_nesting);
|
217 |
} |
218 |
return g_test_run();
|
219 |
} |