Asterisk - The Open Source Telephony Project GIT-master-d856a3e
test_sched.c
Go to the documentation of this file.
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#include "asterisk/astobj2.h"
41
42static int sched_cb(const void *data)
43{
44 return 0;
45}
46
47static int order_check;
49
50static void sched_order_check(struct ast_test *test, int order)
51{
53 if (order_check != order) {
54 ast_test_status_update(test, "Unexpected execution order: expected:%d got:%d\n",
57 }
58}
59
60static int sched_order_1_cb(const void *data)
61{
62 sched_order_check((void *) data, 1);
63 return 0;
64}
65
66static int sched_order_2_cb(const void *data)
67{
68 sched_order_check((void *) data, 2);
69 return 0;
70}
71
72static int sched_order_3_cb(const void *data)
73{
74 sched_order_check((void *) data, 3);
75 return 0;
76}
77
78static int sched_order_4_cb(const void *data)
79{
80 sched_order_check((void *) data, 4);
81 return 0;
82}
83
84static int sched_order_5_cb(const void *data)
85{
86 sched_order_check((void *) data, 5);
87 return 0;
88}
89
90static int sched_order_6_cb(const void *data)
91{
92 sched_order_check((void *) data, 6);
93 return 0;
94}
95
96static int sched_order_7_cb(const void *data)
97{
98 sched_order_check((void *) data, 7);
99 return 0;
100}
101
102static int sched_order_8_cb(const void *data)
103{
104 sched_order_check((void *) data, 8);
105 return 0;
106}
107
108AST_TEST_DEFINE(sched_test_order)
109{
110 struct ast_sched_context *con;
112 int id1, id2, id3, wait;
113
114 switch (cmd) {
115 case TEST_INIT:
116 info->name = "sched_test_order";
117 info->category = "/main/sched/";
118 info->summary = "Test ordering of events in the scheduler API";
119 info->description =
120 "This test ensures that events are properly ordered by the "
121 "time they are scheduled to execute in the scheduler API.";
122 return AST_TEST_NOT_RUN;
123 case TEST_EXECUTE:
124 break;
125 }
126
127 if (!(con = ast_sched_context_create())) {
129 "Test failed - could not create scheduler context\n");
130 return AST_TEST_FAIL;
131 }
132
133 /* Add 3 scheduler entries, and then remove them, ensuring that the result
134 * of ast_sched_wait() looks appropriate at each step along the way. */
135
136 if ((wait = ast_sched_wait(con)) != -1) {
138 "ast_sched_wait() should have returned -1, returned '%d'\n",
139 wait);
140 goto return_cleanup;
141 }
142
143 if ((id1 = ast_sched_add(con, 100000, sched_cb, NULL)) == -1) {
144 ast_test_status_update(test, "Failed to add scheduler entry\n");
145 goto return_cleanup;
146 }
147
148 if ((wait = ast_sched_wait(con)) > 100000) {
150 "ast_sched_wait() should have returned <= 100000, returned '%d'\n",
151 wait);
152 goto return_cleanup;
153 }
154
155 if ((id2 = ast_sched_add(con, 10000, sched_cb, NULL)) == -1) {
156 ast_test_status_update(test, "Failed to add scheduler entry\n");
157 goto return_cleanup;
158 }
159
160 if ((wait = ast_sched_wait(con)) > 10000) {
162 "ast_sched_wait() should have returned <= 10000, returned '%d'\n",
163 wait);
164 goto return_cleanup;
165 }
166
167 if ((id3 = ast_sched_add(con, 1000, sched_cb, NULL)) == -1) {
168 ast_test_status_update(test, "Failed to add scheduler entry\n");
169 goto return_cleanup;
170 }
171
172 if ((wait = ast_sched_wait(con)) > 1000) {
174 "ast_sched_wait() should have returned <= 1000, returned '%d'\n",
175 wait);
176 goto return_cleanup;
177 }
178
179 if (ast_sched_del(con, id3) == -1) {
180 ast_test_status_update(test, "Failed to remove scheduler entry\n");
181 goto return_cleanup;
182 }
183
184 if ((wait = ast_sched_wait(con)) <= 1000) {
186 "ast_sched_wait() should have returned > 1000, returned '%d'\n",
187 wait);
188 goto return_cleanup;
189 }
190
191 if (ast_sched_del(con, id2) == -1) {
192 ast_test_status_update(test, "Failed to remove scheduler entry\n");
193 goto return_cleanup;
194 }
195
196 if ((wait = ast_sched_wait(con)) <= 10000) {
198 "ast_sched_wait() should have returned > 10000, returned '%d'\n",
199 wait);
200 goto return_cleanup;
201 }
202
203 if (ast_sched_del(con, id1) == -1) {
204 ast_test_status_update(test, "Failed to remove scheduler entry\n");
205 goto return_cleanup;
206 }
207
208 if ((wait = ast_sched_wait(con)) != -1) {
210 "ast_sched_wait() should have returned -1, returned '%d'\n",
211 wait);
212 goto return_cleanup;
213 }
214
215 /*
216 * Schedule immediate and delayed entries to check the order
217 * that they get executed. They must get executed at the
218 * time they expire in the order they were added.
219 */
220#define DELAYED_SAME_EXPIRE 300 /* ms */
221 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_1_cb, test), res, return_cleanup);
222 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_1_cb, test), res, return_cleanup);
223 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_2_cb, test), res, return_cleanup);
224 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_2_cb, test), res, return_cleanup);
225 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_3_cb, test), res, return_cleanup);
226 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_3_cb, test), res, return_cleanup);
227 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_4_cb, test), res, return_cleanup);
228 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_4_cb, test), res, return_cleanup);
229 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_5_cb, test), res, return_cleanup);
230 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_5_cb, test), res, return_cleanup);
231 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_6_cb, test), res, return_cleanup);
232 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_6_cb, test), res, return_cleanup);
233 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_7_cb, test), res, return_cleanup);
234 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, 0, sched_order_7_cb, test), res, return_cleanup);
235 ast_test_validate_cleanup(test, -1 < ast_sched_add(con, DELAYED_SAME_EXPIRE, sched_order_8_cb, test), res, return_cleanup);
236
237 /* Check order of scheduled immediate entries. */
238 order_check = 0;
240 usleep(50 * 1000);/* Ensure that all the immediate entries are ready to expire */
241 ast_test_validate_cleanup(test, 7 == ast_sched_runq(con), res, return_cleanup);
242 ast_test_validate_cleanup(test, !order_check_failed, res, return_cleanup);
243
244 /* Check order of scheduled entries expiring at the same time. */
245 order_check = 0;
247 usleep((DELAYED_SAME_EXPIRE + 50) * 1000);/* Ensure that all the delayed entries are ready to expire */
248 ast_test_validate_cleanup(test, 8 == ast_sched_runq(con), res, return_cleanup);
249 ast_test_validate_cleanup(test, !order_check_failed, res, return_cleanup);
250
251 if ((wait = ast_sched_wait(con)) != -1) {
253 "ast_sched_wait() should have returned -1, returned '%d'\n",
254 wait);
255 goto return_cleanup;
256 }
257
258 res = AST_TEST_PASS;
259
260return_cleanup:
262
263 return res;
264}
265
266static char *handle_cli_sched_bench(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
267{
268 struct ast_sched_context *con;
269 struct timeval start;
270 unsigned int num, i;
271 int *sched_ids = NULL;
272
273 switch (cmd) {
274 case CLI_INIT:
275 e->command = "sched benchmark";
276 e->usage = ""
277 "Usage: sched benchmark <num>\n"
278 "";
279 return NULL;
280 case CLI_GENERATE:
281 return NULL;
282 }
283
284 if (a->argc != e->args + 1) {
285 return CLI_SHOWUSAGE;
286 }
287
288 if (sscanf(a->argv[e->args], "%u", &num) != 1) {
289 return CLI_SHOWUSAGE;
290 }
291
292 if (!(con = ast_sched_context_create())) {
293 ast_cli(a->fd, "Test failed - could not create scheduler context\n");
294 return CLI_FAILURE;
295 }
296
297 if (!(sched_ids = ast_malloc(sizeof(*sched_ids) * num))) {
298 ast_cli(a->fd, "Test failed - memory allocation failure\n");
299 goto return_cleanup;
300 }
301
302 ast_cli(a->fd, "Testing ast_sched_add() performance - timing how long it takes "
303 "to add %u entries at random time intervals from 0 to 60 seconds\n", num);
304
305 start = ast_tvnow();
306
307 for (i = 0; i < num; i++) {
308 long when = labs(ast_random()) % 60000;
309 if ((sched_ids[i] = ast_sched_add(con, when, sched_cb, NULL)) == -1) {
310 ast_cli(a->fd, "Test failed - sched_add returned -1\n");
311 goto return_cleanup;
312 }
313 }
314
315 ast_cli(a->fd, "Test complete - %" PRIi64 " us\n", ast_tvdiff_us(ast_tvnow(), start));
316
317 ast_cli(a->fd, "Testing ast_sched_del() performance - timing how long it takes "
318 "to delete %u entries with random time intervals from 0 to 60 seconds\n", num);
319
320 start = ast_tvnow();
321
322 for (i = 0; i < num; i++) {
323 if (ast_sched_del(con, sched_ids[i]) == -1) {
324 ast_cli(a->fd, "Test failed - sched_del returned -1\n");
325 goto return_cleanup;
326 }
327 }
328
329 ast_cli(a->fd, "Test complete - %" PRIi64 " us\n", ast_tvdiff_us(ast_tvnow(), start));
330
331return_cleanup:
333 if (sched_ids) {
334 ast_free(sched_ids);
335 }
336
337 return CLI_SUCCESS;
338}
339
340struct test_obj {
344 int id;
345};
346
347static void test_obj_cleanup(void *data)
348{
349 struct test_obj *obj = data;
350 ast_mutex_destroy(&obj->lock);
351 ast_cond_destroy(&obj->cond);
352}
353
354static int lockingcb(const void *data)
355{
356 struct test_obj *obj = (struct test_obj *)data;
357 struct timespec delay = {3,0};
358
359 ast_mutex_lock(&obj->lock);
360
361 obj->scheduledCBstarted = 1;
362 ast_cond_signal(&obj->cond);
363
364 ast_mutex_unlock(&obj->lock);
365
366 ao2_ref(obj, -1);
367 while (nanosleep(&delay, &delay));
368 /* sleep to force this scheduled event to remain running long
369 * enough for the scheduling thread to unlock and call
370 * AST_SCHED_DEL_UNREF
371 */
372
373 return 0;
374}
375
376AST_TEST_DEFINE(sched_test_freebird)
377{
378 struct test_obj * obj;
379 struct ast_sched_context * con;
381 int refs;
382
383 switch (cmd) {
384 case TEST_INIT:
385 info->name = "sched_test_freebird";
386 info->category = "/main/sched/";
387 info->summary = "Test deadlock avoidance and double-unref";
388 info->description =
389 "This tests a call to AST_SCHED_DEL_UNREF on a running event.";
390 return AST_TEST_NOT_RUN;
391 case TEST_EXECUTE:
392 res = AST_TEST_PASS;
393 break;
394 }
395
396 obj = ao2_alloc(sizeof(struct test_obj), test_obj_cleanup);
397 if (!obj) {
399 "ao2_alloc() did not return an object\n");
400 return AST_TEST_FAIL;
401 }
402
403 obj->scheduledCBstarted = 0;
404
406 if (!con) {
408 "ast_sched_context_create() did not return a context\n");
409 ao2_cleanup(obj);
410 return AST_TEST_FAIL;
411 }
412
413 if (ast_sched_start_thread(con)) {
414 ast_test_status_update(test, "Failed to start test thread\n");
415 ao2_cleanup(obj);
417 return AST_TEST_FAIL;
418 }
419
420 /* This double reference is to ensure that the object isn't destroyed prematurely
421 * in a case where it is unreffed an additional time.
422 */
423 ao2_ref(obj, +2);
424 if ((obj->id = ast_sched_add(con, 0, lockingcb, obj)) == -1) {
425 ast_test_status_update(test, "Failed to add scheduler entry\n");
426 ao2_ref(obj, -3);
428 return AST_TEST_FAIL;
429 }
430
431 ast_mutex_lock(&obj->lock);
432 while(obj->scheduledCBstarted == 0) {
433 /* Wait for the scheduled thread to indicate that it has started so we can
434 * then call the AST_SCHED_DEL_UNREF macro
435 */
436 ast_cond_wait(&obj->cond,&obj->lock);
437 }
438 ast_mutex_unlock(&obj->lock);
439
440 ast_test_status_update(test, "Received signal, calling Scedule and UNREF\n");
441 ast_test_status_update(test, "ID: %d\n", obj->id);
442 AST_SCHED_DEL_UNREF(con, obj->id, ao2_ref(obj, -1));
443
444 refs = ao2_ref(obj, 0);
445
446 switch(refs){
447 case 2:
448 ast_test_status_update(test, "Correct number of references '2'\n");
449 break;
450 default:
451 ast_test_status_update(test, "Incorrect number of references '%d'\n",
452 refs);
453 res = AST_TEST_FAIL;
454 break;
455 }
456
457 /* Based on success or failure, the refcount could change
458 */
459 while(ao2_ref(obj, -1) > 1);
460
462
463 return res;
464}
465
466static struct ast_cli_entry cli_sched[] = {
467 AST_CLI_DEFINE(handle_cli_sched_bench, "Benchmark ast_sched add/del performance"),
468};
469
470static int unload_module(void)
471{
472 AST_TEST_UNREGISTER(sched_test_order);
473 AST_TEST_UNREGISTER(sched_test_freebird);
475 return 0;
476}
477
478static int load_module(void)
479{
480 AST_TEST_REGISTER(sched_test_order);
481 AST_TEST_REGISTER(sched_test_freebird);
484}
485
486AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ast_sched performance test module");
integer order
Definition: analys.c:66
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_cond_destroy(cond)
Definition: lock.h:202
#define ast_cond_wait(cond, mutex)
Definition: lock.h:205
#define ast_mutex_unlock(a)
Definition: lock.h:190
pthread_cond_t ast_cond_t
Definition: lock.h:178
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
#define ast_cond_signal(cond)
Definition: lock.h:203
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
def info(msg)
#define NULL
Definition: resample.c:96
Scheduler Routines (derived from cheops)
#define AST_SCHED_DEL_UNREF(sched, id, refcall)
schedule task to get deleted and call unref function
Definition: sched.h:82
int ast_sched_del(struct ast_sched_context *con, int id) attribute_warn_unused_result
Deletes a scheduled event.
Definition: sched.c:614
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
Definition: sched.c:567
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:271
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition: sched.c:197
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:786
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:238
int ast_sched_wait(struct ast_sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place.
Definition: sched.c:433
descriptor for a cli entry.
Definition: cli.h:171
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Structure for mutex and tracking information.
Definition: lock.h:135
ast_cond_t cond
Definition: test_sched.c:342
ast_mutex_t lock
Definition: test_sched.c:341
int scheduledCBstarted
Definition: test_sched.c:343
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
static struct test_val a
static struct ast_cli_entry cli_sched[]
Definition: test_sched.c:466
static int order_check
Definition: test_sched.c:47
AST_TEST_DEFINE(sched_test_order)
Definition: test_sched.c:108
static void sched_order_check(struct ast_test *test, int order)
Definition: test_sched.c:50
static int sched_order_7_cb(const void *data)
Definition: test_sched.c:96
static int lockingcb(const void *data)
Definition: test_sched.c:354
#define DELAYED_SAME_EXPIRE
static void test_obj_cleanup(void *data)
Definition: test_sched.c:347
static int sched_order_2_cb(const void *data)
Definition: test_sched.c:66
static int sched_order_6_cb(const void *data)
Definition: test_sched.c:90
static int order_check_failed
Definition: test_sched.c:48
static int sched_cb(const void *data)
Definition: test_sched.c:42
static int sched_order_5_cb(const void *data)
Definition: test_sched.c:84
static int sched_order_8_cb(const void *data)
Definition: test_sched.c:102
static char * handle_cli_sched_bench(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: test_sched.c:266
static int sched_order_3_cb(const void *data)
Definition: test_sched.c:72
static int load_module(void)
Definition: test_sched.c:478
static int unload_module(void)
Definition: test_sched.c:470
static int sched_order_4_cb(const void *data)
Definition: test_sched.c:78
static int sched_order_1_cb(const void *data)
Definition: test_sched.c:60
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:87
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Utility functions.
long int ast_random(void)
Definition: utils.c:2312
#define ARRAY_LEN(a)
Definition: utils.h:666