Merge "lpc10: Avoid compiler warning when DONT_OPTIMIZE/COMPILE_DOUBLE."
[asterisk/asterisk.git] / tests / test_sched.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, Digium, Inc.
5  *
6  * Russell Bryant <russell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief ast_sched performance test module
22  *
23  * \author Russell Bryant <russell@digium.com>
24  */
25
26 /*** MODULEINFO
27         <depend>TEST_FRAMEWORK</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 #include <inttypes.h>
34
35 #include "asterisk/module.h"
36 #include "asterisk/utils.h"
37 #include "asterisk/sched.h"
38 #include "asterisk/test.h"
39 #include "asterisk/cli.h"
40
41 static int sched_cb(const void *data)
42 {
43         return 0;
44 }
45
46 static int order_check;
47 static int order_check_failed;
48
49 static void sched_order_check(struct ast_test *test, int order)
50 {
51         ++order_check;
52         if (order_check != order) {
53                 ast_test_status_update(test, "Unexpected execution order: expected:%d got:%d\n",
54                         order, order_check);
55                 order_check_failed = 1;
56         }
57 }
58
59 static int sched_order_1_cb(const void *data)
60 {
61         sched_order_check((void *) data, 1);
62         return 0;
63 }
64
65 static int sched_order_2_cb(const void *data)
66 {
67         sched_order_check((void *) data, 2);
68         return 0;
69 }
70
71 static int sched_order_3_cb(const void *data)
72 {
73         sched_order_check((void *) data, 3);
74         return 0;
75 }
76
77 static int sched_order_4_cb(const void *data)
78 {
79         sched_order_check((void *) data, 4);
80         return 0;
81 }
82
83 static int sched_order_5_cb(const void *data)
84 {
85         sched_order_check((void *) data, 5);
86         return 0;
87 }
88
89 static int sched_order_6_cb(const void *data)
90 {
91         sched_order_check((void *) data, 6);
92         return 0;
93 }
94
95 static int sched_order_7_cb(const void *data)
96 {
97         sched_order_check((void *) data, 7);
98         return 0;
99 }
100
101 static int sched_order_8_cb(const void *data)
102 {
103         sched_order_check((void *) data, 8);
104         return 0;
105 }
106
107 AST_TEST_DEFINE(sched_test_order)
108 {
109         struct ast_sched_context *con;
110         enum ast_test_result_state res = AST_TEST_FAIL;
111         int id1, id2, id3, wait;
112
113         switch (cmd) {
114         case TEST_INIT:
115                 info->name = "sched_test_order";
116                 info->category = "/main/sched/";
117                 info->summary = "Test ordering of events in the scheduler API";
118                 info->description =
119                         "This test ensures that events are properly ordered by the "
120                         "time they are scheduled to execute in the scheduler API.";
121                 return AST_TEST_NOT_RUN;
122         case TEST_EXECUTE:
123                 break;
124         }
125
126         if (!(con = ast_sched_context_create())) {
127                 ast_test_status_update(test,
128                                 "Test failed - could not create scheduler context\n");
129                 return AST_TEST_FAIL;
130         }
131
132         /* Add 3 scheduler entries, and then remove them, ensuring that the result
133          * of ast_sched_wait() looks appropriate at each step along the way. */
134
135         if ((wait = ast_sched_wait(con)) != -1) {
136                 ast_test_status_update(test,
137                                 "ast_sched_wait() should have returned -1, returned '%d'\n",
138                                 wait);
139                 goto return_cleanup;
140         }
141
142         if ((id1 = ast_sched_add(con, 100000, sched_cb, NULL)) == -1) {
143                 ast_test_status_update(test, "Failed to add scheduler entry\n");
144                 goto return_cleanup;
145         }
146
147         if ((wait = ast_sched_wait(con)) > 100000) {
148                 ast_test_status_update(test,
149                                 "ast_sched_wait() should have returned <= 100000, returned '%d'\n",
150                                 wait);
151                 goto return_cleanup;
152         }
153
154         if ((id2 = ast_sched_add(con, 10000, sched_cb, NULL)) == -1) {
155                 ast_test_status_update(test, "Failed to add scheduler entry\n");
156                 goto return_cleanup;
157         }
158
159         if ((wait = ast_sched_wait(con)) > 10000) {
160                 ast_test_status_update(test,
161                                 "ast_sched_wait() should have returned <= 10000, returned '%d'\n",
162                                 wait);
163                 goto return_cleanup;
164         }
165
166         if ((id3 = ast_sched_add(con, 1000, sched_cb, NULL)) == -1) {
167                 ast_test_status_update(test, "Failed to add scheduler entry\n");
168                 goto return_cleanup;
169         }
170
171         if ((wait = ast_sched_wait(con)) > 1000) {
172                 ast_test_status_update(test,
173                                 "ast_sched_wait() should have returned <= 1000, returned '%d'\n",
174                                 wait);
175                 goto return_cleanup;
176         }
177
178         if (ast_sched_del(con, id3) == -1) {
179                 ast_test_status_update(test, "Failed to remove scheduler entry\n");
180                 goto return_cleanup;
181         }
182
183         if ((wait = ast_sched_wait(con)) <= 1000) {
184                 ast_test_status_update(test,
185                                 "ast_sched_wait() should have returned > 1000, returned '%d'\n",
186                                 wait);
187                 goto return_cleanup;
188         }
189
190         if (ast_sched_del(con, id2) == -1) {
191                 ast_test_status_update(test, "Failed to remove scheduler entry\n");
192                 goto return_cleanup;
193         }
194
195         if ((wait = ast_sched_wait(con)) <= 10000) {
196                 ast_test_status_update(test,
197                                 "ast_sched_wait() should have returned > 10000, returned '%d'\n",
198                                 wait);
199                 goto return_cleanup;
200         }
201
202         if (ast_sched_del(con, id1) == -1) {
203                 ast_test_status_update(test, "Failed to remove scheduler entry\n");
204                 goto return_cleanup;
205         }
206
207         if ((wait = ast_sched_wait(con)) != -1) {
208                 ast_test_status_update(test,
209                                 "ast_sched_wait() should have returned -1, returned '%d'\n",
210                                 wait);
211                 goto return_cleanup;
212         }
213
214         /*
215          * Schedule immediate and delayed entries to check the order
216          * that they get executed.  They must get executed at the
217          * time they expire in the order they were added.
218          */
219 #define DELAYED_SAME_EXPIRE             300 /* ms */
220         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_1_cb, test), res, return_cleanup);
221         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_1_cb, test), res, return_cleanup);
222         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_2_cb, test), res, return_cleanup);
223         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_2_cb, test), res, return_cleanup);
224         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_3_cb, test), res, return_cleanup);
225         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_3_cb, test), res, return_cleanup);
226         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_4_cb, test), res, return_cleanup);
227         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_4_cb, test), res, return_cleanup);
228         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_5_cb, test), res, return_cleanup);
229         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_5_cb, test), res, return_cleanup);
230         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_6_cb, test), res, return_cleanup);
231         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_6_cb, test), res, return_cleanup);
232         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_7_cb, test), res, return_cleanup);
233         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_7_cb, test), res, return_cleanup);
234         ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_8_cb, test), res, return_cleanup);
235
236         /* Check order of scheduled immediate entries. */
237         order_check = 0;
238         order_check_failed = 0;
239         usleep(50 * 1000);/* Ensure that all the immediate entries are ready to expire */
240         ast_test_validate_cleanup(test, 7 == ast_sched_runq(con), res, return_cleanup);
241         ast_test_validate_cleanup(test, !order_check_failed, res, return_cleanup);
242
243         /* Check order of scheduled entries expiring at the same time. */
244         order_check = 0;
245         order_check_failed = 0;
246         usleep((DELAYED_SAME_EXPIRE + 50) * 1000);/* Ensure that all the delayed entries are ready to expire */
247         ast_test_validate_cleanup(test, 8 == ast_sched_runq(con), res, return_cleanup);
248         ast_test_validate_cleanup(test, !order_check_failed, res, return_cleanup);
249
250         if ((wait = ast_sched_wait(con)) != -1) {
251                 ast_test_status_update(test,
252                                 "ast_sched_wait() should have returned -1, returned '%d'\n",
253                                 wait);
254                 goto return_cleanup;
255         }
256
257         res = AST_TEST_PASS;
258
259 return_cleanup:
260         ast_sched_context_destroy(con);
261
262         return res;
263 }
264
265 static char *handle_cli_sched_bench(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
266 {
267         struct ast_sched_context *con;
268         struct timeval start;
269         unsigned int num, i;
270         int *sched_ids = NULL;
271
272         switch (cmd) {
273         case CLI_INIT:
274                 e->command = "sched benchmark";
275                 e->usage = ""
276                         "Usage: sched benchmark <num>\n"
277                         "";
278                 return NULL;
279         case CLI_GENERATE:
280                 return NULL;
281         }
282
283         if (a->argc != e->args + 1) {
284                 return CLI_SHOWUSAGE;
285         }
286
287         if (sscanf(a->argv[e->args], "%u", &num) != 1) {
288                 return CLI_SHOWUSAGE;
289         }
290
291         if (!(con = ast_sched_context_create())) {
292                 ast_cli(a->fd, "Test failed - could not create scheduler context\n");
293                 return CLI_FAILURE;
294         }
295
296         if (!(sched_ids = ast_malloc(sizeof(*sched_ids) * num))) {
297                 ast_cli(a->fd, "Test failed - memory allocation failure\n");
298                 goto return_cleanup;
299         }
300
301         ast_cli(a->fd, "Testing ast_sched_add() performance - timing how long it takes "
302                         "to add %u entries at random time intervals from 0 to 60 seconds\n", num);
303
304         start = ast_tvnow();
305
306         for (i = 0; i < num; i++) {
307                 long when = labs(ast_random()) % 60000;
308                 if ((sched_ids[i] = ast_sched_add(con, when, sched_cb, NULL)) == -1) {
309                         ast_cli(a->fd, "Test failed - sched_add returned -1\n");
310                         goto return_cleanup;
311                 }
312         }
313
314         ast_cli(a->fd, "Test complete - %" PRIi64 " us\n", ast_tvdiff_us(ast_tvnow(), start));
315
316         ast_cli(a->fd, "Testing ast_sched_del() performance - timing how long it takes "
317                         "to delete %u entries with random time intervals from 0 to 60 seconds\n", num);
318
319         start = ast_tvnow();
320
321         for (i = 0; i < num; i++) {
322                 if (ast_sched_del(con, sched_ids[i]) == -1) {
323                         ast_cli(a->fd, "Test failed - sched_del returned -1\n");
324                         goto return_cleanup;
325                 }
326         }
327
328         ast_cli(a->fd, "Test complete - %" PRIi64 " us\n", ast_tvdiff_us(ast_tvnow(), start));
329
330 return_cleanup:
331         ast_sched_context_destroy(con);
332         if (sched_ids) {
333                 ast_free(sched_ids);
334         }
335
336         return CLI_SUCCESS;
337 }
338
339 static struct ast_cli_entry cli_sched[] = {
340         AST_CLI_DEFINE(handle_cli_sched_bench, "Benchmark ast_sched add/del performance"),
341 };
342
343 static int unload_module(void)
344 {
345         AST_TEST_UNREGISTER(sched_test_order);
346         ast_cli_unregister_multiple(cli_sched, ARRAY_LEN(cli_sched));
347         return 0;
348 }
349
350 static int load_module(void)
351 {
352         AST_TEST_REGISTER(sched_test_order);
353         ast_cli_register_multiple(cli_sched, ARRAY_LEN(cli_sched));
354         return AST_MODULE_LOAD_SUCCESS;
355 }
356
357 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ast_sched performance test module");