1da668aa1SThomas Huth /*
2da668aa1SThomas Huth * Coroutine tests
3da668aa1SThomas Huth *
4da668aa1SThomas Huth * Copyright IBM, Corp. 2011
5da668aa1SThomas Huth *
6da668aa1SThomas Huth * Authors:
7da668aa1SThomas Huth * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
8da668aa1SThomas Huth *
9da668aa1SThomas Huth * This work is licensed under the terms of the GNU LGPL, version 2 or later.
10da668aa1SThomas Huth * See the COPYING.LIB file in the top-level directory.
11da668aa1SThomas Huth *
12da668aa1SThomas Huth */
13da668aa1SThomas Huth
14da668aa1SThomas Huth #include "qemu/osdep.h"
15da668aa1SThomas Huth #include "qemu/coroutine_int.h"
16da668aa1SThomas Huth
17da668aa1SThomas Huth /*
18da668aa1SThomas Huth * Check that qemu_in_coroutine() works
19da668aa1SThomas Huth */
20da668aa1SThomas Huth
verify_in_coroutine(void * opaque)21da668aa1SThomas Huth static void coroutine_fn verify_in_coroutine(void *opaque)
22da668aa1SThomas Huth {
23da668aa1SThomas Huth g_assert(qemu_in_coroutine());
24da668aa1SThomas Huth }
25da668aa1SThomas Huth
test_in_coroutine(void)26da668aa1SThomas Huth static void test_in_coroutine(void)
27da668aa1SThomas Huth {
28da668aa1SThomas Huth Coroutine *coroutine;
29da668aa1SThomas Huth
30da668aa1SThomas Huth g_assert(!qemu_in_coroutine());
31da668aa1SThomas Huth
32da668aa1SThomas Huth coroutine = qemu_coroutine_create(verify_in_coroutine, NULL);
33da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
34da668aa1SThomas Huth }
35da668aa1SThomas Huth
36da668aa1SThomas Huth /*
37da668aa1SThomas Huth * Check that qemu_coroutine_self() works
38da668aa1SThomas Huth */
39da668aa1SThomas Huth
verify_self(void * opaque)40da668aa1SThomas Huth static void coroutine_fn verify_self(void *opaque)
41da668aa1SThomas Huth {
42da668aa1SThomas Huth Coroutine **p_co = opaque;
43da668aa1SThomas Huth g_assert(qemu_coroutine_self() == *p_co);
44da668aa1SThomas Huth }
45da668aa1SThomas Huth
test_self(void)46da668aa1SThomas Huth static void test_self(void)
47da668aa1SThomas Huth {
48da668aa1SThomas Huth Coroutine *coroutine;
49da668aa1SThomas Huth
50da668aa1SThomas Huth coroutine = qemu_coroutine_create(verify_self, &coroutine);
51da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
52da668aa1SThomas Huth }
53da668aa1SThomas Huth
54da668aa1SThomas Huth /*
55da668aa1SThomas Huth * Check that qemu_coroutine_entered() works
56da668aa1SThomas Huth */
57da668aa1SThomas Huth
verify_entered_step_2(void * opaque)58da668aa1SThomas Huth static void coroutine_fn verify_entered_step_2(void *opaque)
59da668aa1SThomas Huth {
60da668aa1SThomas Huth Coroutine *caller = (Coroutine *)opaque;
61da668aa1SThomas Huth
62da668aa1SThomas Huth g_assert(qemu_coroutine_entered(caller));
63da668aa1SThomas Huth g_assert(qemu_coroutine_entered(qemu_coroutine_self()));
64da668aa1SThomas Huth qemu_coroutine_yield();
65da668aa1SThomas Huth
66da668aa1SThomas Huth /* Once more to check it still works after yielding */
67da668aa1SThomas Huth g_assert(qemu_coroutine_entered(caller));
68da668aa1SThomas Huth g_assert(qemu_coroutine_entered(qemu_coroutine_self()));
69da668aa1SThomas Huth }
70da668aa1SThomas Huth
verify_entered_step_1(void * opaque)71da668aa1SThomas Huth static void coroutine_fn verify_entered_step_1(void *opaque)
72da668aa1SThomas Huth {
73da668aa1SThomas Huth Coroutine *self = qemu_coroutine_self();
74da668aa1SThomas Huth Coroutine *coroutine;
75da668aa1SThomas Huth
76da668aa1SThomas Huth g_assert(qemu_coroutine_entered(self));
77da668aa1SThomas Huth
78da668aa1SThomas Huth coroutine = qemu_coroutine_create(verify_entered_step_2, self);
79da668aa1SThomas Huth g_assert(!qemu_coroutine_entered(coroutine));
80da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
81da668aa1SThomas Huth g_assert(!qemu_coroutine_entered(coroutine));
82da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
83da668aa1SThomas Huth }
84da668aa1SThomas Huth
test_entered(void)85da668aa1SThomas Huth static void test_entered(void)
86da668aa1SThomas Huth {
87da668aa1SThomas Huth Coroutine *coroutine;
88da668aa1SThomas Huth
89da668aa1SThomas Huth coroutine = qemu_coroutine_create(verify_entered_step_1, NULL);
90da668aa1SThomas Huth g_assert(!qemu_coroutine_entered(coroutine));
91da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
92da668aa1SThomas Huth }
93da668aa1SThomas Huth
94da668aa1SThomas Huth /*
95da668aa1SThomas Huth * Check that coroutines may nest multiple levels
96da668aa1SThomas Huth */
97da668aa1SThomas Huth
98da668aa1SThomas Huth typedef struct {
99da668aa1SThomas Huth unsigned int n_enter; /* num coroutines entered */
100da668aa1SThomas Huth unsigned int n_return; /* num coroutines returned */
101da668aa1SThomas Huth unsigned int max; /* maximum level of nesting */
102da668aa1SThomas Huth } NestData;
103da668aa1SThomas Huth
nest(void * opaque)104da668aa1SThomas Huth static void coroutine_fn nest(void *opaque)
105da668aa1SThomas Huth {
106da668aa1SThomas Huth NestData *nd = opaque;
107da668aa1SThomas Huth
108da668aa1SThomas Huth nd->n_enter++;
109da668aa1SThomas Huth
110da668aa1SThomas Huth if (nd->n_enter < nd->max) {
111da668aa1SThomas Huth Coroutine *child;
112da668aa1SThomas Huth
113da668aa1SThomas Huth child = qemu_coroutine_create(nest, nd);
114da668aa1SThomas Huth qemu_coroutine_enter(child);
115da668aa1SThomas Huth }
116da668aa1SThomas Huth
117da668aa1SThomas Huth nd->n_return++;
118da668aa1SThomas Huth }
119da668aa1SThomas Huth
test_nesting(void)120da668aa1SThomas Huth static void test_nesting(void)
121da668aa1SThomas Huth {
122da668aa1SThomas Huth Coroutine *root;
123da668aa1SThomas Huth NestData nd = {
124da668aa1SThomas Huth .n_enter = 0,
125da668aa1SThomas Huth .n_return = 0,
126da668aa1SThomas Huth .max = 128,
127da668aa1SThomas Huth };
128da668aa1SThomas Huth
129da668aa1SThomas Huth root = qemu_coroutine_create(nest, &nd);
130da668aa1SThomas Huth qemu_coroutine_enter(root);
131da668aa1SThomas Huth
132da668aa1SThomas Huth /* Must enter and return from max nesting level */
133da668aa1SThomas Huth g_assert_cmpint(nd.n_enter, ==, nd.max);
134da668aa1SThomas Huth g_assert_cmpint(nd.n_return, ==, nd.max);
135da668aa1SThomas Huth }
136da668aa1SThomas Huth
137da668aa1SThomas Huth /*
138da668aa1SThomas Huth * Check that yield/enter transfer control correctly
139da668aa1SThomas Huth */
140da668aa1SThomas Huth
yield_5_times(void * opaque)141da668aa1SThomas Huth static void coroutine_fn yield_5_times(void *opaque)
142da668aa1SThomas Huth {
143da668aa1SThomas Huth bool *done = opaque;
144da668aa1SThomas Huth int i;
145da668aa1SThomas Huth
146da668aa1SThomas Huth for (i = 0; i < 5; i++) {
147da668aa1SThomas Huth qemu_coroutine_yield();
148da668aa1SThomas Huth }
149da668aa1SThomas Huth *done = true;
150da668aa1SThomas Huth }
151da668aa1SThomas Huth
test_yield(void)152da668aa1SThomas Huth static void test_yield(void)
153da668aa1SThomas Huth {
154da668aa1SThomas Huth Coroutine *coroutine;
155da668aa1SThomas Huth bool done = false;
156da668aa1SThomas Huth int i = -1; /* one extra time to return from coroutine */
157da668aa1SThomas Huth
158da668aa1SThomas Huth coroutine = qemu_coroutine_create(yield_5_times, &done);
159da668aa1SThomas Huth while (!done) {
160da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
161da668aa1SThomas Huth i++;
162da668aa1SThomas Huth }
163da668aa1SThomas Huth g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
164da668aa1SThomas Huth }
165da668aa1SThomas Huth
c2_fn(void * opaque)166da668aa1SThomas Huth static void coroutine_fn c2_fn(void *opaque)
167da668aa1SThomas Huth {
168da668aa1SThomas Huth qemu_coroutine_yield();
169da668aa1SThomas Huth }
170da668aa1SThomas Huth
c1_fn(void * opaque)171da668aa1SThomas Huth static void coroutine_fn c1_fn(void *opaque)
172da668aa1SThomas Huth {
173da668aa1SThomas Huth Coroutine *c2 = opaque;
174da668aa1SThomas Huth qemu_coroutine_enter(c2);
175da668aa1SThomas Huth }
176da668aa1SThomas Huth
test_no_dangling_access(void)177da668aa1SThomas Huth static void test_no_dangling_access(void)
178da668aa1SThomas Huth {
179da668aa1SThomas Huth Coroutine *c1;
180da668aa1SThomas Huth Coroutine *c2;
181da668aa1SThomas Huth Coroutine tmp;
182da668aa1SThomas Huth
183da668aa1SThomas Huth c2 = qemu_coroutine_create(c2_fn, NULL);
184da668aa1SThomas Huth c1 = qemu_coroutine_create(c1_fn, c2);
185da668aa1SThomas Huth
186da668aa1SThomas Huth qemu_coroutine_enter(c1);
187da668aa1SThomas Huth
188da668aa1SThomas Huth /* c1 shouldn't be used any more now; make sure we segfault if it is */
189da668aa1SThomas Huth tmp = *c1;
190da668aa1SThomas Huth memset(c1, 0xff, sizeof(Coroutine));
191da668aa1SThomas Huth qemu_coroutine_enter(c2);
192da668aa1SThomas Huth
193da668aa1SThomas Huth /* Must restore the coroutine now to avoid corrupted pool */
194da668aa1SThomas Huth *c1 = tmp;
195da668aa1SThomas Huth }
196da668aa1SThomas Huth
197da668aa1SThomas Huth static bool locked;
198*89c90405SPhilippe Mathieu-Daudé static int done_count;
199da668aa1SThomas Huth
mutex_fn(void * opaque)200da668aa1SThomas Huth static void coroutine_fn mutex_fn(void *opaque)
201da668aa1SThomas Huth {
202da668aa1SThomas Huth CoMutex *m = opaque;
203da668aa1SThomas Huth qemu_co_mutex_lock(m);
204da668aa1SThomas Huth assert(!locked);
205da668aa1SThomas Huth locked = true;
206da668aa1SThomas Huth qemu_coroutine_yield();
207da668aa1SThomas Huth locked = false;
208da668aa1SThomas Huth qemu_co_mutex_unlock(m);
209*89c90405SPhilippe Mathieu-Daudé done_count++;
210da668aa1SThomas Huth }
211da668aa1SThomas Huth
lockable_fn(void * opaque)212da668aa1SThomas Huth static void coroutine_fn lockable_fn(void *opaque)
213da668aa1SThomas Huth {
214da668aa1SThomas Huth QemuLockable *x = opaque;
215da668aa1SThomas Huth qemu_lockable_lock(x);
216da668aa1SThomas Huth assert(!locked);
217da668aa1SThomas Huth locked = true;
218da668aa1SThomas Huth qemu_coroutine_yield();
219da668aa1SThomas Huth locked = false;
220da668aa1SThomas Huth qemu_lockable_unlock(x);
221*89c90405SPhilippe Mathieu-Daudé done_count++;
222da668aa1SThomas Huth }
223da668aa1SThomas Huth
do_test_co_mutex(CoroutineEntry * entry,void * opaque)224da668aa1SThomas Huth static void do_test_co_mutex(CoroutineEntry *entry, void *opaque)
225da668aa1SThomas Huth {
226da668aa1SThomas Huth Coroutine *c1 = qemu_coroutine_create(entry, opaque);
227da668aa1SThomas Huth Coroutine *c2 = qemu_coroutine_create(entry, opaque);
228da668aa1SThomas Huth
229*89c90405SPhilippe Mathieu-Daudé done_count = 0;
230da668aa1SThomas Huth qemu_coroutine_enter(c1);
231da668aa1SThomas Huth g_assert(locked);
232da668aa1SThomas Huth qemu_coroutine_enter(c2);
233da668aa1SThomas Huth
234da668aa1SThomas Huth /* Unlock queues c2. It is then started automatically when c1 yields or
235da668aa1SThomas Huth * terminates.
236da668aa1SThomas Huth */
237da668aa1SThomas Huth qemu_coroutine_enter(c1);
238*89c90405SPhilippe Mathieu-Daudé g_assert_cmpint(done_count, ==, 1);
239da668aa1SThomas Huth g_assert(locked);
240da668aa1SThomas Huth
241da668aa1SThomas Huth qemu_coroutine_enter(c2);
242*89c90405SPhilippe Mathieu-Daudé g_assert_cmpint(done_count, ==, 2);
243da668aa1SThomas Huth g_assert(!locked);
244da668aa1SThomas Huth }
245da668aa1SThomas Huth
test_co_mutex(void)246da668aa1SThomas Huth static void test_co_mutex(void)
247da668aa1SThomas Huth {
248da668aa1SThomas Huth CoMutex m;
249da668aa1SThomas Huth
250da668aa1SThomas Huth qemu_co_mutex_init(&m);
251da668aa1SThomas Huth do_test_co_mutex(mutex_fn, &m);
252da668aa1SThomas Huth }
253da668aa1SThomas Huth
test_co_mutex_lockable(void)254da668aa1SThomas Huth static void test_co_mutex_lockable(void)
255da668aa1SThomas Huth {
256da668aa1SThomas Huth CoMutex m;
257da668aa1SThomas Huth CoMutex *null_pointer = NULL;
258da668aa1SThomas Huth
259da668aa1SThomas Huth qemu_co_mutex_init(&m);
260da668aa1SThomas Huth do_test_co_mutex(lockable_fn, QEMU_MAKE_LOCKABLE(&m));
261da668aa1SThomas Huth
262da668aa1SThomas Huth g_assert(QEMU_MAKE_LOCKABLE(null_pointer) == NULL);
263da668aa1SThomas Huth }
264da668aa1SThomas Huth
26525bc2daeSPaolo Bonzini static CoRwlock rwlock;
26625bc2daeSPaolo Bonzini
26725bc2daeSPaolo Bonzini /* Test that readers are properly sent back to the queue when upgrading,
26825bc2daeSPaolo Bonzini * even if they are the sole readers. The test scenario is as follows:
26925bc2daeSPaolo Bonzini *
27025bc2daeSPaolo Bonzini *
27125bc2daeSPaolo Bonzini * | c1 | c2 |
27225bc2daeSPaolo Bonzini * |--------------+------------+
27325bc2daeSPaolo Bonzini * | rdlock | |
27425bc2daeSPaolo Bonzini * | yield | |
27525bc2daeSPaolo Bonzini * | | wrlock |
27625bc2daeSPaolo Bonzini * | | <queued> |
27725bc2daeSPaolo Bonzini * | upgrade | |
27825bc2daeSPaolo Bonzini * | <queued> | <dequeued> |
27925bc2daeSPaolo Bonzini * | | unlock |
28025bc2daeSPaolo Bonzini * | <dequeued> | |
28125bc2daeSPaolo Bonzini * | unlock | |
28225bc2daeSPaolo Bonzini */
28325bc2daeSPaolo Bonzini
rwlock_yield_upgrade(void * opaque)28425bc2daeSPaolo Bonzini static void coroutine_fn rwlock_yield_upgrade(void *opaque)
28525bc2daeSPaolo Bonzini {
28625bc2daeSPaolo Bonzini qemu_co_rwlock_rdlock(&rwlock);
28725bc2daeSPaolo Bonzini qemu_coroutine_yield();
28825bc2daeSPaolo Bonzini
28925bc2daeSPaolo Bonzini qemu_co_rwlock_upgrade(&rwlock);
29025bc2daeSPaolo Bonzini qemu_co_rwlock_unlock(&rwlock);
29125bc2daeSPaolo Bonzini
29225bc2daeSPaolo Bonzini *(bool *)opaque = true;
29325bc2daeSPaolo Bonzini }
29425bc2daeSPaolo Bonzini
rwlock_wrlock_yield(void * opaque)29525bc2daeSPaolo Bonzini static void coroutine_fn rwlock_wrlock_yield(void *opaque)
29625bc2daeSPaolo Bonzini {
29725bc2daeSPaolo Bonzini qemu_co_rwlock_wrlock(&rwlock);
29825bc2daeSPaolo Bonzini qemu_coroutine_yield();
29925bc2daeSPaolo Bonzini
30025bc2daeSPaolo Bonzini qemu_co_rwlock_unlock(&rwlock);
30125bc2daeSPaolo Bonzini *(bool *)opaque = true;
30225bc2daeSPaolo Bonzini }
30325bc2daeSPaolo Bonzini
test_co_rwlock_upgrade(void)30425bc2daeSPaolo Bonzini static void test_co_rwlock_upgrade(void)
30525bc2daeSPaolo Bonzini {
30625bc2daeSPaolo Bonzini bool c1_done = false;
30725bc2daeSPaolo Bonzini bool c2_done = false;
30825bc2daeSPaolo Bonzini Coroutine *c1, *c2;
30925bc2daeSPaolo Bonzini
31025bc2daeSPaolo Bonzini qemu_co_rwlock_init(&rwlock);
31125bc2daeSPaolo Bonzini c1 = qemu_coroutine_create(rwlock_yield_upgrade, &c1_done);
31225bc2daeSPaolo Bonzini c2 = qemu_coroutine_create(rwlock_wrlock_yield, &c2_done);
31325bc2daeSPaolo Bonzini
31425bc2daeSPaolo Bonzini qemu_coroutine_enter(c1);
31525bc2daeSPaolo Bonzini qemu_coroutine_enter(c2);
31625bc2daeSPaolo Bonzini
31725bc2daeSPaolo Bonzini /* c1 now should go to sleep. */
31825bc2daeSPaolo Bonzini qemu_coroutine_enter(c1);
31925bc2daeSPaolo Bonzini g_assert(!c1_done);
32025bc2daeSPaolo Bonzini
32125bc2daeSPaolo Bonzini qemu_coroutine_enter(c2);
32225bc2daeSPaolo Bonzini g_assert(c1_done);
32325bc2daeSPaolo Bonzini g_assert(c2_done);
32425bc2daeSPaolo Bonzini }
32525bc2daeSPaolo Bonzini
rwlock_rdlock_yield(void * opaque)326b6489ac0SDavid Edmondson static void coroutine_fn rwlock_rdlock_yield(void *opaque)
327b6489ac0SDavid Edmondson {
328b6489ac0SDavid Edmondson qemu_co_rwlock_rdlock(&rwlock);
329b6489ac0SDavid Edmondson qemu_coroutine_yield();
330b6489ac0SDavid Edmondson
331b6489ac0SDavid Edmondson qemu_co_rwlock_unlock(&rwlock);
332b6489ac0SDavid Edmondson qemu_coroutine_yield();
333b6489ac0SDavid Edmondson
334b6489ac0SDavid Edmondson *(bool *)opaque = true;
335b6489ac0SDavid Edmondson }
336b6489ac0SDavid Edmondson
rwlock_wrlock_downgrade(void * opaque)337b6489ac0SDavid Edmondson static void coroutine_fn rwlock_wrlock_downgrade(void *opaque)
338b6489ac0SDavid Edmondson {
339b6489ac0SDavid Edmondson qemu_co_rwlock_wrlock(&rwlock);
340b6489ac0SDavid Edmondson
341b6489ac0SDavid Edmondson qemu_co_rwlock_downgrade(&rwlock);
342b6489ac0SDavid Edmondson qemu_co_rwlock_unlock(&rwlock);
343b6489ac0SDavid Edmondson *(bool *)opaque = true;
344b6489ac0SDavid Edmondson }
345b6489ac0SDavid Edmondson
rwlock_rdlock(void * opaque)346b6489ac0SDavid Edmondson static void coroutine_fn rwlock_rdlock(void *opaque)
347b6489ac0SDavid Edmondson {
348b6489ac0SDavid Edmondson qemu_co_rwlock_rdlock(&rwlock);
349b6489ac0SDavid Edmondson
350b6489ac0SDavid Edmondson qemu_co_rwlock_unlock(&rwlock);
351b6489ac0SDavid Edmondson *(bool *)opaque = true;
352b6489ac0SDavid Edmondson }
353b6489ac0SDavid Edmondson
rwlock_wrlock(void * opaque)354b6489ac0SDavid Edmondson static void coroutine_fn rwlock_wrlock(void *opaque)
355b6489ac0SDavid Edmondson {
356b6489ac0SDavid Edmondson qemu_co_rwlock_wrlock(&rwlock);
357b6489ac0SDavid Edmondson
358b6489ac0SDavid Edmondson qemu_co_rwlock_unlock(&rwlock);
359b6489ac0SDavid Edmondson *(bool *)opaque = true;
360b6489ac0SDavid Edmondson }
361b6489ac0SDavid Edmondson
362b6489ac0SDavid Edmondson /*
363b6489ac0SDavid Edmondson * Check that downgrading a reader-writer lock does not cause a hang.
364b6489ac0SDavid Edmondson *
365b6489ac0SDavid Edmondson * Four coroutines are used to produce a situation where there are
366b6489ac0SDavid Edmondson * both reader and writer hopefuls waiting to acquire an rwlock that
367b6489ac0SDavid Edmondson * is held by a reader.
368b6489ac0SDavid Edmondson *
369b6489ac0SDavid Edmondson * The correct sequence of operations we aim to provoke can be
370b6489ac0SDavid Edmondson * represented as:
371b6489ac0SDavid Edmondson *
372b6489ac0SDavid Edmondson * | c1 | c2 | c3 | c4 |
373b6489ac0SDavid Edmondson * |--------+------------+------------+------------|
374b6489ac0SDavid Edmondson * | rdlock | | | |
375b6489ac0SDavid Edmondson * | yield | | | |
376b6489ac0SDavid Edmondson * | | wrlock | | |
377b6489ac0SDavid Edmondson * | | <queued> | | |
378b6489ac0SDavid Edmondson * | | | rdlock | |
379b6489ac0SDavid Edmondson * | | | <queued> | |
380b6489ac0SDavid Edmondson * | | | | wrlock |
381b6489ac0SDavid Edmondson * | | | | <queued> |
382b6489ac0SDavid Edmondson * | unlock | | | |
383b6489ac0SDavid Edmondson * | yield | | | |
384b6489ac0SDavid Edmondson * | | <dequeued> | | |
385b6489ac0SDavid Edmondson * | | downgrade | | |
386b6489ac0SDavid Edmondson * | | | <dequeued> | |
387b6489ac0SDavid Edmondson * | | | unlock | |
388b6489ac0SDavid Edmondson * | | ... | | |
389b6489ac0SDavid Edmondson * | | unlock | | |
390b6489ac0SDavid Edmondson * | | | | <dequeued> |
391b6489ac0SDavid Edmondson * | | | | unlock |
392b6489ac0SDavid Edmondson */
test_co_rwlock_downgrade(void)393b6489ac0SDavid Edmondson static void test_co_rwlock_downgrade(void)
394b6489ac0SDavid Edmondson {
395b6489ac0SDavid Edmondson bool c1_done = false;
396b6489ac0SDavid Edmondson bool c2_done = false;
397b6489ac0SDavid Edmondson bool c3_done = false;
398b6489ac0SDavid Edmondson bool c4_done = false;
399b6489ac0SDavid Edmondson Coroutine *c1, *c2, *c3, *c4;
400b6489ac0SDavid Edmondson
401b6489ac0SDavid Edmondson qemu_co_rwlock_init(&rwlock);
402b6489ac0SDavid Edmondson
403b6489ac0SDavid Edmondson c1 = qemu_coroutine_create(rwlock_rdlock_yield, &c1_done);
404b6489ac0SDavid Edmondson c2 = qemu_coroutine_create(rwlock_wrlock_downgrade, &c2_done);
405b6489ac0SDavid Edmondson c3 = qemu_coroutine_create(rwlock_rdlock, &c3_done);
406b6489ac0SDavid Edmondson c4 = qemu_coroutine_create(rwlock_wrlock, &c4_done);
407b6489ac0SDavid Edmondson
408b6489ac0SDavid Edmondson qemu_coroutine_enter(c1);
409b6489ac0SDavid Edmondson qemu_coroutine_enter(c2);
410b6489ac0SDavid Edmondson qemu_coroutine_enter(c3);
411b6489ac0SDavid Edmondson qemu_coroutine_enter(c4);
412b6489ac0SDavid Edmondson
413b6489ac0SDavid Edmondson qemu_coroutine_enter(c1);
414b6489ac0SDavid Edmondson
415b6489ac0SDavid Edmondson g_assert(c2_done);
416b6489ac0SDavid Edmondson g_assert(c3_done);
417b6489ac0SDavid Edmondson g_assert(c4_done);
418b6489ac0SDavid Edmondson
419b6489ac0SDavid Edmondson qemu_coroutine_enter(c1);
420b6489ac0SDavid Edmondson
421b6489ac0SDavid Edmondson g_assert(c1_done);
422b6489ac0SDavid Edmondson }
423b6489ac0SDavid Edmondson
424da668aa1SThomas Huth /*
425da668aa1SThomas Huth * Check that creation, enter, and return work
426da668aa1SThomas Huth */
427da668aa1SThomas Huth
set_and_exit(void * opaque)428da668aa1SThomas Huth static void coroutine_fn set_and_exit(void *opaque)
429da668aa1SThomas Huth {
430da668aa1SThomas Huth bool *done = opaque;
431da668aa1SThomas Huth
432da668aa1SThomas Huth *done = true;
433da668aa1SThomas Huth }
434da668aa1SThomas Huth
test_lifecycle(void)435da668aa1SThomas Huth static void test_lifecycle(void)
436da668aa1SThomas Huth {
437da668aa1SThomas Huth Coroutine *coroutine;
438da668aa1SThomas Huth bool done = false;
439da668aa1SThomas Huth
440da668aa1SThomas Huth /* Create, enter, and return from coroutine */
441da668aa1SThomas Huth coroutine = qemu_coroutine_create(set_and_exit, &done);
442da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
443da668aa1SThomas Huth g_assert(done); /* expect done to be true (first time) */
444da668aa1SThomas Huth
445da668aa1SThomas Huth /* Repeat to check that no state affects this test */
446da668aa1SThomas Huth done = false;
447da668aa1SThomas Huth coroutine = qemu_coroutine_create(set_and_exit, &done);
448da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
449da668aa1SThomas Huth g_assert(done); /* expect done to be true (second time) */
450da668aa1SThomas Huth }
451da668aa1SThomas Huth
452da668aa1SThomas Huth
453da668aa1SThomas Huth #define RECORD_SIZE 10 /* Leave some room for expansion */
454da668aa1SThomas Huth struct coroutine_position {
455da668aa1SThomas Huth int func;
456da668aa1SThomas Huth int state;
457da668aa1SThomas Huth };
458da668aa1SThomas Huth static struct coroutine_position records[RECORD_SIZE];
459da668aa1SThomas Huth static unsigned record_pos;
460da668aa1SThomas Huth
record_push(int func,int state)461da668aa1SThomas Huth static void record_push(int func, int state)
462da668aa1SThomas Huth {
463da668aa1SThomas Huth struct coroutine_position *cp = &records[record_pos++];
464da668aa1SThomas Huth g_assert_cmpint(record_pos, <, RECORD_SIZE);
465da668aa1SThomas Huth cp->func = func;
466da668aa1SThomas Huth cp->state = state;
467da668aa1SThomas Huth }
468da668aa1SThomas Huth
co_order_test(void * opaque)469da668aa1SThomas Huth static void coroutine_fn co_order_test(void *opaque)
470da668aa1SThomas Huth {
471da668aa1SThomas Huth record_push(2, 1);
472da668aa1SThomas Huth g_assert(qemu_in_coroutine());
473da668aa1SThomas Huth qemu_coroutine_yield();
474da668aa1SThomas Huth record_push(2, 2);
475da668aa1SThomas Huth g_assert(qemu_in_coroutine());
476da668aa1SThomas Huth }
477da668aa1SThomas Huth
do_order_test(void)478da668aa1SThomas Huth static void do_order_test(void)
479da668aa1SThomas Huth {
480da668aa1SThomas Huth Coroutine *co;
481da668aa1SThomas Huth
482da668aa1SThomas Huth co = qemu_coroutine_create(co_order_test, NULL);
483da668aa1SThomas Huth record_push(1, 1);
484da668aa1SThomas Huth qemu_coroutine_enter(co);
485da668aa1SThomas Huth record_push(1, 2);
486da668aa1SThomas Huth g_assert(!qemu_in_coroutine());
487da668aa1SThomas Huth qemu_coroutine_enter(co);
488da668aa1SThomas Huth record_push(1, 3);
489da668aa1SThomas Huth g_assert(!qemu_in_coroutine());
490da668aa1SThomas Huth }
491da668aa1SThomas Huth
test_order(void)492da668aa1SThomas Huth static void test_order(void)
493da668aa1SThomas Huth {
494da668aa1SThomas Huth int i;
495da668aa1SThomas Huth const struct coroutine_position expected_pos[] = {
496da668aa1SThomas Huth {1, 1,}, {2, 1}, {1, 2}, {2, 2}, {1, 3}
497da668aa1SThomas Huth };
498da668aa1SThomas Huth do_order_test();
499da668aa1SThomas Huth g_assert_cmpint(record_pos, ==, 5);
500da668aa1SThomas Huth for (i = 0; i < record_pos; i++) {
501da668aa1SThomas Huth g_assert_cmpint(records[i].func , ==, expected_pos[i].func );
502da668aa1SThomas Huth g_assert_cmpint(records[i].state, ==, expected_pos[i].state);
503da668aa1SThomas Huth }
504da668aa1SThomas Huth }
505da668aa1SThomas Huth /*
506da668aa1SThomas Huth * Lifecycle benchmark
507da668aa1SThomas Huth */
508da668aa1SThomas Huth
empty_coroutine(void * opaque)509da668aa1SThomas Huth static void coroutine_fn empty_coroutine(void *opaque)
510da668aa1SThomas Huth {
511da668aa1SThomas Huth /* Do nothing */
512da668aa1SThomas Huth }
513da668aa1SThomas Huth
perf_lifecycle(void)514da668aa1SThomas Huth static void perf_lifecycle(void)
515da668aa1SThomas Huth {
516da668aa1SThomas Huth Coroutine *coroutine;
517da668aa1SThomas Huth unsigned int i, max;
518da668aa1SThomas Huth double duration;
519da668aa1SThomas Huth
520da668aa1SThomas Huth max = 1000000;
521da668aa1SThomas Huth
522da668aa1SThomas Huth g_test_timer_start();
523da668aa1SThomas Huth for (i = 0; i < max; i++) {
524da668aa1SThomas Huth coroutine = qemu_coroutine_create(empty_coroutine, NULL);
525da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
526da668aa1SThomas Huth }
527da668aa1SThomas Huth duration = g_test_timer_elapsed();
528da668aa1SThomas Huth
529da668aa1SThomas Huth g_test_message("Lifecycle %u iterations: %f s", max, duration);
530da668aa1SThomas Huth }
531da668aa1SThomas Huth
perf_nesting(void)532da668aa1SThomas Huth static void perf_nesting(void)
533da668aa1SThomas Huth {
534da668aa1SThomas Huth unsigned int i, maxcycles, maxnesting;
535da668aa1SThomas Huth double duration;
536da668aa1SThomas Huth
537da668aa1SThomas Huth maxcycles = 10000;
538da668aa1SThomas Huth maxnesting = 1000;
539da668aa1SThomas Huth Coroutine *root;
540da668aa1SThomas Huth
541da668aa1SThomas Huth g_test_timer_start();
542da668aa1SThomas Huth for (i = 0; i < maxcycles; i++) {
543da668aa1SThomas Huth NestData nd = {
544da668aa1SThomas Huth .n_enter = 0,
545da668aa1SThomas Huth .n_return = 0,
546da668aa1SThomas Huth .max = maxnesting,
547da668aa1SThomas Huth };
548da668aa1SThomas Huth root = qemu_coroutine_create(nest, &nd);
549da668aa1SThomas Huth qemu_coroutine_enter(root);
550da668aa1SThomas Huth }
551da668aa1SThomas Huth duration = g_test_timer_elapsed();
552da668aa1SThomas Huth
553da668aa1SThomas Huth g_test_message("Nesting %u iterations of %u depth each: %f s",
554da668aa1SThomas Huth maxcycles, maxnesting, duration);
555da668aa1SThomas Huth }
556da668aa1SThomas Huth
557da668aa1SThomas Huth /*
558da668aa1SThomas Huth * Yield benchmark
559da668aa1SThomas Huth */
560da668aa1SThomas Huth
yield_loop(void * opaque)561da668aa1SThomas Huth static void coroutine_fn yield_loop(void *opaque)
562da668aa1SThomas Huth {
563da668aa1SThomas Huth unsigned int *counter = opaque;
564da668aa1SThomas Huth
565da668aa1SThomas Huth while ((*counter) > 0) {
566da668aa1SThomas Huth (*counter)--;
567da668aa1SThomas Huth qemu_coroutine_yield();
568da668aa1SThomas Huth }
569da668aa1SThomas Huth }
570da668aa1SThomas Huth
perf_yield(void)571da668aa1SThomas Huth static void perf_yield(void)
572da668aa1SThomas Huth {
573da668aa1SThomas Huth unsigned int i, maxcycles;
574da668aa1SThomas Huth double duration;
575da668aa1SThomas Huth
576da668aa1SThomas Huth maxcycles = 100000000;
577da668aa1SThomas Huth i = maxcycles;
578da668aa1SThomas Huth Coroutine *coroutine = qemu_coroutine_create(yield_loop, &i);
579da668aa1SThomas Huth
580da668aa1SThomas Huth g_test_timer_start();
581da668aa1SThomas Huth while (i > 0) {
582da668aa1SThomas Huth qemu_coroutine_enter(coroutine);
583da668aa1SThomas Huth }
584da668aa1SThomas Huth duration = g_test_timer_elapsed();
585da668aa1SThomas Huth
586da668aa1SThomas Huth g_test_message("Yield %u iterations: %f s", maxcycles, duration);
587da668aa1SThomas Huth }
588da668aa1SThomas Huth
dummy(unsigned * i)589da668aa1SThomas Huth static __attribute__((noinline)) void dummy(unsigned *i)
590da668aa1SThomas Huth {
591da668aa1SThomas Huth (*i)--;
592da668aa1SThomas Huth }
593da668aa1SThomas Huth
perf_baseline(void)594da668aa1SThomas Huth static void perf_baseline(void)
595da668aa1SThomas Huth {
596da668aa1SThomas Huth unsigned int i, maxcycles;
597da668aa1SThomas Huth double duration;
598da668aa1SThomas Huth
599da668aa1SThomas Huth maxcycles = 100000000;
600da668aa1SThomas Huth i = maxcycles;
601da668aa1SThomas Huth
602da668aa1SThomas Huth g_test_timer_start();
603da668aa1SThomas Huth while (i > 0) {
604da668aa1SThomas Huth dummy(&i);
605da668aa1SThomas Huth }
606da668aa1SThomas Huth duration = g_test_timer_elapsed();
607da668aa1SThomas Huth
608da668aa1SThomas Huth g_test_message("Function call %u iterations: %f s", maxcycles, duration);
609da668aa1SThomas Huth }
610da668aa1SThomas Huth
perf_cost_func(void * opaque)611f7bbb156SMarc-André Lureau static __attribute__((noinline)) void coroutine_fn perf_cost_func(void *opaque)
612da668aa1SThomas Huth {
613da668aa1SThomas Huth qemu_coroutine_yield();
614da668aa1SThomas Huth }
615da668aa1SThomas Huth
perf_cost(void)616da668aa1SThomas Huth static void perf_cost(void)
617da668aa1SThomas Huth {
618da668aa1SThomas Huth const unsigned long maxcycles = 40000000;
619da668aa1SThomas Huth unsigned long i = 0;
620da668aa1SThomas Huth double duration;
621da668aa1SThomas Huth unsigned long ops;
622da668aa1SThomas Huth Coroutine *co;
623da668aa1SThomas Huth
624da668aa1SThomas Huth g_test_timer_start();
625da668aa1SThomas Huth while (i++ < maxcycles) {
626da668aa1SThomas Huth co = qemu_coroutine_create(perf_cost_func, &i);
627da668aa1SThomas Huth qemu_coroutine_enter(co);
628da668aa1SThomas Huth qemu_coroutine_enter(co);
629da668aa1SThomas Huth }
630da668aa1SThomas Huth duration = g_test_timer_elapsed();
631da668aa1SThomas Huth ops = (long)(maxcycles / (duration * 1000));
632da668aa1SThomas Huth
633da668aa1SThomas Huth g_test_message("Run operation %lu iterations %f s, %luK operations/s, "
634da668aa1SThomas Huth "%luns per coroutine",
635da668aa1SThomas Huth maxcycles,
636da668aa1SThomas Huth duration, ops,
637da668aa1SThomas Huth (unsigned long)(1000000000.0 * duration / maxcycles));
638da668aa1SThomas Huth }
639da668aa1SThomas Huth
main(int argc,char ** argv)640da668aa1SThomas Huth int main(int argc, char **argv)
641da668aa1SThomas Huth {
642da668aa1SThomas Huth g_test_init(&argc, &argv, NULL);
643da668aa1SThomas Huth
644da668aa1SThomas Huth /* This test assumes there is a freelist and marks freed coroutine memory
645da668aa1SThomas Huth * with a sentinel value. If there is no freelist this would legitimately
646da668aa1SThomas Huth * crash, so skip it.
647da668aa1SThomas Huth */
648230f6e06SPaolo Bonzini if (IS_ENABLED(CONFIG_COROUTINE_POOL)) {
649da668aa1SThomas Huth g_test_add_func("/basic/no-dangling-access", test_no_dangling_access);
650da668aa1SThomas Huth }
651da668aa1SThomas Huth
652da668aa1SThomas Huth g_test_add_func("/basic/lifecycle", test_lifecycle);
653da668aa1SThomas Huth g_test_add_func("/basic/yield", test_yield);
654da668aa1SThomas Huth g_test_add_func("/basic/nesting", test_nesting);
655da668aa1SThomas Huth g_test_add_func("/basic/self", test_self);
656da668aa1SThomas Huth g_test_add_func("/basic/entered", test_entered);
657da668aa1SThomas Huth g_test_add_func("/basic/in_coroutine", test_in_coroutine);
658da668aa1SThomas Huth g_test_add_func("/basic/order", test_order);
659da668aa1SThomas Huth g_test_add_func("/locking/co-mutex", test_co_mutex);
660da668aa1SThomas Huth g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable);
66125bc2daeSPaolo Bonzini g_test_add_func("/locking/co-rwlock/upgrade", test_co_rwlock_upgrade);
662b6489ac0SDavid Edmondson g_test_add_func("/locking/co-rwlock/downgrade", test_co_rwlock_downgrade);
663da668aa1SThomas Huth if (g_test_perf()) {
664da668aa1SThomas Huth g_test_add_func("/perf/lifecycle", perf_lifecycle);
665da668aa1SThomas Huth g_test_add_func("/perf/nesting", perf_nesting);
666da668aa1SThomas Huth g_test_add_func("/perf/yield", perf_yield);
667da668aa1SThomas Huth g_test_add_func("/perf/function-call", perf_baseline);
668da668aa1SThomas Huth g_test_add_func("/perf/cost", perf_cost);
669da668aa1SThomas Huth }
670da668aa1SThomas Huth return g_test_run();
671da668aa1SThomas Huth }
672