Statistics
| Branch: | Revision:

root / tests / test-coroutine.c @ b3ce604e

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 "qemu-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
}