1
2
3
4
5
6
7
8
9
10
11
12
13
14#include "qemu/osdep.h"
15#include "qemu/coroutine.h"
16#include "qemu/coroutine_int.h"
17#include "qemu/lockable.h"
18
19
20
21
22
23static void coroutine_fn verify_in_coroutine(void *opaque)
24{
25 g_assert(qemu_in_coroutine());
26}
27
28static void test_in_coroutine(void)
29{
30 Coroutine *coroutine;
31
32 g_assert(!qemu_in_coroutine());
33
34 coroutine = qemu_coroutine_create(verify_in_coroutine, NULL);
35 qemu_coroutine_enter(coroutine);
36}
37
38
39
40
41
42static void coroutine_fn verify_self(void *opaque)
43{
44 Coroutine **p_co = opaque;
45 g_assert(qemu_coroutine_self() == *p_co);
46}
47
48static void test_self(void)
49{
50 Coroutine *coroutine;
51
52 coroutine = qemu_coroutine_create(verify_self, &coroutine);
53 qemu_coroutine_enter(coroutine);
54}
55
56
57
58
59
60static void coroutine_fn verify_entered_step_2(void *opaque)
61{
62 Coroutine *caller = (Coroutine *)opaque;
63
64 g_assert(qemu_coroutine_entered(caller));
65 g_assert(qemu_coroutine_entered(qemu_coroutine_self()));
66 qemu_coroutine_yield();
67
68
69 g_assert(qemu_coroutine_entered(caller));
70 g_assert(qemu_coroutine_entered(qemu_coroutine_self()));
71}
72
73static void coroutine_fn verify_entered_step_1(void *opaque)
74{
75 Coroutine *self = qemu_coroutine_self();
76 Coroutine *coroutine;
77
78 g_assert(qemu_coroutine_entered(self));
79
80 coroutine = qemu_coroutine_create(verify_entered_step_2, self);
81 g_assert(!qemu_coroutine_entered(coroutine));
82 qemu_coroutine_enter(coroutine);
83 g_assert(!qemu_coroutine_entered(coroutine));
84 qemu_coroutine_enter(coroutine);
85}
86
87static void test_entered(void)
88{
89 Coroutine *coroutine;
90
91 coroutine = qemu_coroutine_create(verify_entered_step_1, NULL);
92 g_assert(!qemu_coroutine_entered(coroutine));
93 qemu_coroutine_enter(coroutine);
94}
95
96
97
98
99
100typedef struct {
101 unsigned int n_enter;
102 unsigned int n_return;
103 unsigned int max;
104} NestData;
105
106static void coroutine_fn nest(void *opaque)
107{
108 NestData *nd = opaque;
109
110 nd->n_enter++;
111
112 if (nd->n_enter < nd->max) {
113 Coroutine *child;
114
115 child = qemu_coroutine_create(nest, nd);
116 qemu_coroutine_enter(child);
117 }
118
119 nd->n_return++;
120}
121
122static void test_nesting(void)
123{
124 Coroutine *root;
125 NestData nd = {
126 .n_enter = 0,
127 .n_return = 0,
128 .max = 128,
129 };
130
131 root = qemu_coroutine_create(nest, &nd);
132 qemu_coroutine_enter(root);
133
134
135 g_assert_cmpint(nd.n_enter, ==, nd.max);
136 g_assert_cmpint(nd.n_return, ==, nd.max);
137}
138
139
140
141
142
143static void coroutine_fn yield_5_times(void *opaque)
144{
145 bool *done = opaque;
146 int i;
147
148 for (i = 0; i < 5; i++) {
149 qemu_coroutine_yield();
150 }
151 *done = true;
152}
153
154static void test_yield(void)
155{
156 Coroutine *coroutine;
157 bool done = false;
158 int i = -1;
159
160 coroutine = qemu_coroutine_create(yield_5_times, &done);
161 while (!done) {
162 qemu_coroutine_enter(coroutine);
163 i++;
164 }
165 g_assert_cmpint(i, ==, 5);
166}
167
168static void coroutine_fn c2_fn(void *opaque)
169{
170 qemu_coroutine_yield();
171}
172
173static void coroutine_fn c1_fn(void *opaque)
174{
175 Coroutine *c2 = opaque;
176 qemu_coroutine_enter(c2);
177}
178
179static void test_no_dangling_access(void)
180{
181 Coroutine *c1;
182 Coroutine *c2;
183 Coroutine tmp;
184
185 c2 = qemu_coroutine_create(c2_fn, NULL);
186 c1 = qemu_coroutine_create(c1_fn, c2);
187
188 qemu_coroutine_enter(c1);
189
190
191 tmp = *c1;
192 memset(c1, 0xff, sizeof(Coroutine));
193 qemu_coroutine_enter(c2);
194
195
196 *c1 = tmp;
197}
198
199static bool locked;
200static int done;
201
202static void coroutine_fn mutex_fn(void *opaque)
203{
204 CoMutex *m = opaque;
205 qemu_co_mutex_lock(m);
206 assert(!locked);
207 locked = true;
208 qemu_coroutine_yield();
209 locked = false;
210 qemu_co_mutex_unlock(m);
211 done++;
212}
213
214static void coroutine_fn lockable_fn(void *opaque)
215{
216 QemuLockable *x = opaque;
217 qemu_lockable_lock(x);
218 assert(!locked);
219 locked = true;
220 qemu_coroutine_yield();
221 locked = false;
222 qemu_lockable_unlock(x);
223 done++;
224}
225
226static void do_test_co_mutex(CoroutineEntry *entry, void *opaque)
227{
228 Coroutine *c1 = qemu_coroutine_create(entry, opaque);
229 Coroutine *c2 = qemu_coroutine_create(entry, opaque);
230
231 done = 0;
232 qemu_coroutine_enter(c1);
233 g_assert(locked);
234 qemu_coroutine_enter(c2);
235
236
237
238
239 qemu_coroutine_enter(c1);
240 g_assert_cmpint(done, ==, 1);
241 g_assert(locked);
242
243 qemu_coroutine_enter(c2);
244 g_assert_cmpint(done, ==, 2);
245 g_assert(!locked);
246}
247
248static void test_co_mutex(void)
249{
250 CoMutex m;
251
252 qemu_co_mutex_init(&m);
253 do_test_co_mutex(mutex_fn, &m);
254}
255
256static void test_co_mutex_lockable(void)
257{
258 CoMutex m;
259 CoMutex *null_pointer = NULL;
260
261 qemu_co_mutex_init(&m);
262 do_test_co_mutex(lockable_fn, QEMU_MAKE_LOCKABLE(&m));
263
264 g_assert(QEMU_MAKE_LOCKABLE(null_pointer) == NULL);
265}
266
267static CoRwlock rwlock;
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286static void coroutine_fn rwlock_yield_upgrade(void *opaque)
287{
288 qemu_co_rwlock_rdlock(&rwlock);
289 qemu_coroutine_yield();
290
291 qemu_co_rwlock_upgrade(&rwlock);
292 qemu_co_rwlock_unlock(&rwlock);
293
294 *(bool *)opaque = true;
295}
296
297static void coroutine_fn rwlock_wrlock_yield(void *opaque)
298{
299 qemu_co_rwlock_wrlock(&rwlock);
300 qemu_coroutine_yield();
301
302 qemu_co_rwlock_unlock(&rwlock);
303 *(bool *)opaque = true;
304}
305
306static void test_co_rwlock_upgrade(void)
307{
308 bool c1_done = false;
309 bool c2_done = false;
310 Coroutine *c1, *c2;
311
312 qemu_co_rwlock_init(&rwlock);
313 c1 = qemu_coroutine_create(rwlock_yield_upgrade, &c1_done);
314 c2 = qemu_coroutine_create(rwlock_wrlock_yield, &c2_done);
315
316 qemu_coroutine_enter(c1);
317 qemu_coroutine_enter(c2);
318
319
320 qemu_coroutine_enter(c1);
321 g_assert(!c1_done);
322
323 qemu_coroutine_enter(c2);
324 g_assert(c1_done);
325 g_assert(c2_done);
326}
327
328static void coroutine_fn rwlock_rdlock_yield(void *opaque)
329{
330 qemu_co_rwlock_rdlock(&rwlock);
331 qemu_coroutine_yield();
332
333 qemu_co_rwlock_unlock(&rwlock);
334 qemu_coroutine_yield();
335
336 *(bool *)opaque = true;
337}
338
339static void coroutine_fn rwlock_wrlock_downgrade(void *opaque)
340{
341 qemu_co_rwlock_wrlock(&rwlock);
342
343 qemu_co_rwlock_downgrade(&rwlock);
344 qemu_co_rwlock_unlock(&rwlock);
345 *(bool *)opaque = true;
346}
347
348static void coroutine_fn rwlock_rdlock(void *opaque)
349{
350 qemu_co_rwlock_rdlock(&rwlock);
351
352 qemu_co_rwlock_unlock(&rwlock);
353 *(bool *)opaque = true;
354}
355
356static void coroutine_fn rwlock_wrlock(void *opaque)
357{
358 qemu_co_rwlock_wrlock(&rwlock);
359
360 qemu_co_rwlock_unlock(&rwlock);
361 *(bool *)opaque = true;
362}
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395static void test_co_rwlock_downgrade(void)
396{
397 bool c1_done = false;
398 bool c2_done = false;
399 bool c3_done = false;
400 bool c4_done = false;
401 Coroutine *c1, *c2, *c3, *c4;
402
403 qemu_co_rwlock_init(&rwlock);
404
405 c1 = qemu_coroutine_create(rwlock_rdlock_yield, &c1_done);
406 c2 = qemu_coroutine_create(rwlock_wrlock_downgrade, &c2_done);
407 c3 = qemu_coroutine_create(rwlock_rdlock, &c3_done);
408 c4 = qemu_coroutine_create(rwlock_wrlock, &c4_done);
409
410 qemu_coroutine_enter(c1);
411 qemu_coroutine_enter(c2);
412 qemu_coroutine_enter(c3);
413 qemu_coroutine_enter(c4);
414
415 qemu_coroutine_enter(c1);
416
417 g_assert(c2_done);
418 g_assert(c3_done);
419 g_assert(c4_done);
420
421 qemu_coroutine_enter(c1);
422
423 g_assert(c1_done);
424}
425
426
427
428
429
430static void coroutine_fn set_and_exit(void *opaque)
431{
432 bool *done = opaque;
433
434 *done = true;
435}
436
437static void test_lifecycle(void)
438{
439 Coroutine *coroutine;
440 bool done = false;
441
442
443 coroutine = qemu_coroutine_create(set_and_exit, &done);
444 qemu_coroutine_enter(coroutine);
445 g_assert(done);
446
447
448 done = false;
449 coroutine = qemu_coroutine_create(set_and_exit, &done);
450 qemu_coroutine_enter(coroutine);
451 g_assert(done);
452}
453
454
455#define RECORD_SIZE 10
456struct coroutine_position {
457 int func;
458 int state;
459};
460static struct coroutine_position records[RECORD_SIZE];
461static unsigned record_pos;
462
463static void record_push(int func, int state)
464{
465 struct coroutine_position *cp = &records[record_pos++];
466 g_assert_cmpint(record_pos, <, RECORD_SIZE);
467 cp->func = func;
468 cp->state = state;
469}
470
471static void coroutine_fn co_order_test(void *opaque)
472{
473 record_push(2, 1);
474 g_assert(qemu_in_coroutine());
475 qemu_coroutine_yield();
476 record_push(2, 2);
477 g_assert(qemu_in_coroutine());
478}
479
480static void do_order_test(void)
481{
482 Coroutine *co;
483
484 co = qemu_coroutine_create(co_order_test, NULL);
485 record_push(1, 1);
486 qemu_coroutine_enter(co);
487 record_push(1, 2);
488 g_assert(!qemu_in_coroutine());
489 qemu_coroutine_enter(co);
490 record_push(1, 3);
491 g_assert(!qemu_in_coroutine());
492}
493
494static void test_order(void)
495{
496 int i;
497 const struct coroutine_position expected_pos[] = {
498 {1, 1,}, {2, 1}, {1, 2}, {2, 2}, {1, 3}
499 };
500 do_order_test();
501 g_assert_cmpint(record_pos, ==, 5);
502 for (i = 0; i < record_pos; i++) {
503 g_assert_cmpint(records[i].func , ==, expected_pos[i].func );
504 g_assert_cmpint(records[i].state, ==, expected_pos[i].state);
505 }
506}
507
508
509
510
511static void coroutine_fn empty_coroutine(void *opaque)
512{
513
514}
515
516static void perf_lifecycle(void)
517{
518 Coroutine *coroutine;
519 unsigned int i, max;
520 double duration;
521
522 max = 1000000;
523
524 g_test_timer_start();
525 for (i = 0; i < max; i++) {
526 coroutine = qemu_coroutine_create(empty_coroutine, NULL);
527 qemu_coroutine_enter(coroutine);
528 }
529 duration = g_test_timer_elapsed();
530
531 g_test_message("Lifecycle %u iterations: %f s", max, duration);
532}
533
534static void perf_nesting(void)
535{
536 unsigned int i, maxcycles, maxnesting;
537 double duration;
538
539 maxcycles = 10000;
540 maxnesting = 1000;
541 Coroutine *root;
542
543 g_test_timer_start();
544 for (i = 0; i < maxcycles; i++) {
545 NestData nd = {
546 .n_enter = 0,
547 .n_return = 0,
548 .max = maxnesting,
549 };
550 root = qemu_coroutine_create(nest, &nd);
551 qemu_coroutine_enter(root);
552 }
553 duration = g_test_timer_elapsed();
554
555 g_test_message("Nesting %u iterations of %u depth each: %f s",
556 maxcycles, maxnesting, duration);
557}
558
559
560
561
562
563static void coroutine_fn yield_loop(void *opaque)
564{
565 unsigned int *counter = opaque;
566
567 while ((*counter) > 0) {
568 (*counter)--;
569 qemu_coroutine_yield();
570 }
571}
572
573static void perf_yield(void)
574{
575 unsigned int i, maxcycles;
576 double duration;
577
578 maxcycles = 100000000;
579 i = maxcycles;
580 Coroutine *coroutine = qemu_coroutine_create(yield_loop, &i);
581
582 g_test_timer_start();
583 while (i > 0) {
584 qemu_coroutine_enter(coroutine);
585 }
586 duration = g_test_timer_elapsed();
587
588 g_test_message("Yield %u iterations: %f s", maxcycles, duration);
589}
590
591static __attribute__((noinline)) void dummy(unsigned *i)
592{
593 (*i)--;
594}
595
596static void perf_baseline(void)
597{
598 unsigned int i, maxcycles;
599 double duration;
600
601 maxcycles = 100000000;
602 i = maxcycles;
603
604 g_test_timer_start();
605 while (i > 0) {
606 dummy(&i);
607 }
608 duration = g_test_timer_elapsed();
609
610 g_test_message("Function call %u iterations: %f s", maxcycles, duration);
611}
612
613static __attribute__((noinline)) void perf_cost_func(void *opaque)
614{
615 qemu_coroutine_yield();
616}
617
618static void perf_cost(void)
619{
620 const unsigned long maxcycles = 40000000;
621 unsigned long i = 0;
622 double duration;
623 unsigned long ops;
624 Coroutine *co;
625
626 g_test_timer_start();
627 while (i++ < maxcycles) {
628 co = qemu_coroutine_create(perf_cost_func, &i);
629 qemu_coroutine_enter(co);
630 qemu_coroutine_enter(co);
631 }
632 duration = g_test_timer_elapsed();
633 ops = (long)(maxcycles / (duration * 1000));
634
635 g_test_message("Run operation %lu iterations %f s, %luK operations/s, "
636 "%luns per coroutine",
637 maxcycles,
638 duration, ops,
639 (unsigned long)(1000000000.0 * duration / maxcycles));
640}
641
642int main(int argc, char **argv)
643{
644 g_test_init(&argc, &argv, NULL);
645
646
647
648
649
650 if (CONFIG_COROUTINE_POOL) {
651 g_test_add_func("/basic/no-dangling-access", test_no_dangling_access);
652 }
653
654 g_test_add_func("/basic/lifecycle", test_lifecycle);
655 g_test_add_func("/basic/yield", test_yield);
656 g_test_add_func("/basic/nesting", test_nesting);
657 g_test_add_func("/basic/self", test_self);
658 g_test_add_func("/basic/entered", test_entered);
659 g_test_add_func("/basic/in_coroutine", test_in_coroutine);
660 g_test_add_func("/basic/order", test_order);
661 g_test_add_func("/locking/co-mutex", test_co_mutex);
662 g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable);
663 g_test_add_func("/locking/co-rwlock/upgrade", test_co_rwlock_upgrade);
664 g_test_add_func("/locking/co-rwlock/downgrade", test_co_rwlock_downgrade);
665 if (g_test_perf()) {
666 g_test_add_func("/perf/lifecycle", perf_lifecycle);
667 g_test_add_func("/perf/nesting", perf_nesting);
668 g_test_add_func("/perf/yield", perf_yield);
669 g_test_add_func("/perf/function-call", perf_baseline);
670 g_test_add_func("/perf/cost", perf_cost);
671 }
672 return g_test_run();
673}
674