Asterisk - The Open Source Telephony Project GIT-master-25686a5
test_res_pjsip_scheduler.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2016, Fairview 5 Engineering, LLC
5 *
6 * George Joseph <george.joseph@fairview5.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/*!
20 * \file
21 * \brief res_pjsip scheduler tests
22 *
23 * \author George Joseph <george.joseph@fairview5.com>
24 *
25 */
26
27/*** MODULEINFO
28 <depend>TEST_FRAMEWORK</depend>
29 <depend>pjproject</depend>
30 <depend>res_pjsip</depend>
31 <support_level>core</support_level>
32 ***/
33
34#include "asterisk.h"
35
36#include <pjsip.h>
37#include "asterisk/test.h"
38#include "asterisk/module.h"
40#include "asterisk/res_pjsip.h"
41#include "asterisk/utils.h"
42
43#define CATEGORY "/res/res_pjsip/scheduler/"
44
45struct test_data {
48 pthread_t tid;
49 struct timeval test_start;
50 struct timeval task_start;
51 struct timeval task_end;
54 int sleep;
55 int done;
57 struct ast_test *test;
58};
59
60#define S2U(x) (long int)(x * 1000 * 1000)
61#define M2U(x) (long int)(x * 1000)
62
63static int task_1(void *data)
64{
65 struct test_data *test = data;
66
67 if (!test->no_clear_done) {
68 test->done = 0;
69 }
70 test->task_start = ast_tvnow();
71 test->tid = pthread_self();
72 test->is_servant = ast_sip_thread_is_servant();
73 usleep(M2U(test->sleep));
74 test->task_end = ast_tvnow();
75
76 ast_mutex_lock(&test->lock);
77 test->done++;
78 ast_mutex_unlock(&test->lock);
79 ast_cond_signal(&test->cond);
80
81 return test->interval;
82}
83
84
85static void data_cleanup(void *data)
86{
87 struct test_data *test_data = data;
90}
91
92#define waitfor(x) \
93{ \
94 ast_mutex_lock(&(x)->lock); \
95 while (!(x)->done) { \
96 ast_cond_wait(&(x)->cond, &(x)->lock); \
97 } \
98 (x)->done = 0; \
99 ast_mutex_unlock(&(x)->lock); \
100}
101
102static int scheduler(struct ast_test *test, int serialized)
103{
105 RAII_VAR(struct test_data *, test_data1, ao2_alloc(sizeof(*test_data1), data_cleanup), ao2_cleanup);
106 RAII_VAR(struct test_data *, test_data2, ao2_alloc(sizeof(*test_data2), data_cleanup), ao2_cleanup);
107 RAII_VAR(struct ast_sip_sched_task *, task1, NULL, ao2_cleanup);
108 RAII_VAR(struct ast_sip_sched_task *, task2, NULL, ao2_cleanup);
109 int duration;
110 int delay;
111 struct timeval task1_start;
112
113 ast_test_validate(test, test_data1 != NULL);
114 ast_test_validate(test, test_data2 != NULL);
115
116 test_data1->test = test;
117 test_data1->test_start = ast_tvnow();
118 test_data1->interval = 2000;
119 test_data1->sleep = 1000;
120 ast_mutex_init(&test_data1->lock);
121 ast_cond_init(&test_data1->cond, NULL);
122
123 test_data2->test = test;
124 test_data2->test_start = ast_tvnow();
125 test_data2->interval = 2000;
126 test_data2->sleep = 1000;
127 ast_mutex_init(&test_data2->lock);
128 ast_cond_init(&test_data2->cond, NULL);
129
130 if (serialized) {
131 ast_test_status_update(test, "This test will take about %3.1f seconds\n",
132 (test_data1->interval + test_data1->sleep + (MAX(test_data1->interval - test_data2->interval, 0)) + test_data2->sleep) / 1000.0);
133 tp1 = ast_sip_create_serializer("test-scheduler-serializer");
134 ast_test_validate(test, (tp1 != NULL));
135 } else {
136 ast_test_status_update(test, "This test will take about %3.1f seconds\n",
137 ((MAX(test_data1->interval, test_data2->interval) + MAX(test_data1->sleep, test_data2->sleep)) / 1000.0));
138 }
139
140 task1 = ast_sip_schedule_task(tp1, test_data1->interval, task_1, NULL, test_data1, AST_SIP_SCHED_TASK_FIXED);
141 ast_test_validate(test, task1 != NULL);
142
143 task2 = ast_sip_schedule_task(tp1, test_data2->interval, task_1, NULL, test_data2, AST_SIP_SCHED_TASK_FIXED);
144 ast_test_validate(test, task2 != NULL);
145
146 waitfor(test_data1);
148 ast_test_validate(test, test_data1->is_servant);
149
150 duration = ast_tvdiff_ms(test_data1->task_end, test_data1->test_start);
151 ast_test_validate(test, (duration > ((test_data1->interval + test_data1->sleep) * 0.9))
152 && (duration < ((test_data1->interval + test_data1->sleep) * 1.1)));
153
154 ast_sip_sched_task_get_times(task1, NULL, &task1_start, NULL);
155 delay = ast_tvdiff_ms(task1_start, test_data1->test_start);
156 ast_test_validate(test, (delay > (test_data1->interval * 0.9)
157 && (delay < (test_data1->interval * 1.1))));
158
159 waitfor(test_data2);
161 ast_test_validate(test, test_data2->is_servant);
162
163 if (serialized) {
164 ast_test_validate(test, test_data1->tid == test_data2->tid);
165 ast_test_validate(test, ast_tvdiff_ms(test_data2->task_start, test_data1->task_end) >= 0);
166 } else {
167 ast_test_validate(test, test_data1->tid != test_data2->tid);
168 }
169
170 return AST_TEST_PASS;
171}
172
173AST_TEST_DEFINE(serialized_scheduler)
174{
175
176 switch (cmd) {
177 case TEST_INIT:
178 info->name = __func__;
179 info->category = CATEGORY;
180 info->summary = "Test res_pjsip serialized scheduler";
181 info->description = "Test res_pjsip serialized scheduler";
182 return AST_TEST_NOT_RUN;
183 case TEST_EXECUTE:
184 break;
185 }
186
187 return scheduler(test, 1);
188}
189
190AST_TEST_DEFINE(unserialized_scheduler)
191{
192
193 switch (cmd) {
194 case TEST_INIT:
195 info->name = __func__;
196 info->category = CATEGORY;
197 info->summary = "Test res_pjsip unserialized scheduler";
198 info->description = "Test res_pjsip unserialized scheduler";
199 return AST_TEST_NOT_RUN;
200 case TEST_EXECUTE:
201 break;
202 }
203
204 return scheduler(test, 0);
205}
206
207static int run_count;
208static int destruct_count;
209
210static int dummy_task(void *data)
211{
212 int *sleep = data;
213
214 usleep(M2U(*sleep));
215 run_count++;
216
217 return 0;
218}
219
220static void test_destructor(void *data)
221{
223}
224
225AST_TEST_DEFINE(scheduler_cleanup)
226{
227 RAII_VAR(int *, sleep, NULL, ao2_cleanup);
229 int interval;
230 int when;
231
232 switch (cmd) {
233 case TEST_INIT:
234 info->name = __func__;
235 info->category = CATEGORY;
236 info->summary = "Test res_pjsip scheduler cleanup";
237 info->description = "Test res_pjsip scheduler cleanup";
238 return AST_TEST_NOT_RUN;
239 case TEST_EXECUTE:
240 break;
241 }
242
243 destruct_count = 0;
244 interval = 1000;
245
246 sleep = ao2_alloc(sizeof(*sleep), test_destructor);
247 ast_test_validate(test, sleep != NULL);
248 *sleep = 500;
249
250 ast_test_status_update(test, "This test will take about %3.1f seconds\n",
251 ((interval * 1.1) + *sleep) / 1000.0);
252
253 task = ast_sip_schedule_task(NULL, interval, dummy_task, "dummy", sleep,
255 ast_test_validate(test, task != NULL);
256 usleep(M2U(interval * 0.5));
258 ast_test_validate(test, (when > (interval * 0.4) && when < (interval * 0.6)));
259 usleep(M2U(interval * 0.6));
260 ast_test_validate(test, ast_sip_sched_is_task_running(task));
261
262 usleep(M2U(*sleep));
263
264 ast_test_validate(test, (ast_sip_sched_is_task_running(task) == 0));
266 ast_test_validate(test, (when < 0), res, error);
267 ast_test_validate(test, (ao2_ref(task, 0) == 1));
268 ao2_ref(task, -1);
269 task = NULL;
270 ast_test_validate(test, (destruct_count == 1));
271 sleep = NULL;
272
273 return AST_TEST_PASS;
274}
275
276AST_TEST_DEFINE(scheduler_cancel)
277{
278 RAII_VAR(int *, sleep, NULL, ao2_cleanup);
280 int interval;
281 int when;
282
283 switch (cmd) {
284 case TEST_INIT:
285 info->name = __func__;
286 info->category = CATEGORY;
287 info->summary = "Test res_pjsip scheduler cancel task";
288 info->description = "Test res_pjsip scheduler cancel task";
289 return AST_TEST_NOT_RUN;
290 case TEST_EXECUTE:
291 break;
292 }
293
294 destruct_count = 0;
295 interval = 1000;
296
297 sleep = ao2_alloc(sizeof(*sleep), test_destructor);
298 ast_test_validate(test, sleep != NULL);
299 *sleep = 500;
300
301 ast_test_status_update(test, "This test will take about %3.1f seconds\n",
302 (interval + *sleep) / 1000.0);
303
305 ast_test_validate(test, task != NULL);
306
307 usleep(M2U(interval * 0.5));
309 ast_test_validate(test, (when > (interval * 0.4) && when < (interval * 0.6)));
310 ast_test_validate(test, !ast_sip_sched_is_task_running_by_name("dummy"));
311 ast_test_validate(test, ao2_ref(task, 0) == 2);
312
314
316 ast_test_validate(test, when < 0);
317
318 usleep(M2U(interval));
319 ast_test_validate(test, run_count == 0);
320 ast_test_validate(test, destruct_count == 0);
321 ast_test_validate(test, ao2_ref(task, 0) == 1);
322
323 return AST_TEST_PASS;
324}
325
326AST_TEST_DEFINE(scheduler_policy)
327{
328 RAII_VAR(struct test_data *, test_data1, ao2_alloc(sizeof(*test_data1), data_cleanup), ao2_cleanup);
330 int when;
331
332 switch (cmd) {
333 case TEST_INIT:
334 info->name = __func__;
335 info->category = CATEGORY;
336 info->summary = "Test res_pjsip scheduler cancel task";
337 info->description = "Test res_pjsip scheduler cancel task";
338 return AST_TEST_NOT_RUN;
339 case TEST_EXECUTE:
340 break;
341 }
342
343 ast_test_validate(test, test_data1 != NULL);
344
345 destruct_count = 0;
346 run_count = 0;
347 test_data1->test = test;
348 test_data1->test_start = ast_tvnow();
349 test_data1->interval = 1000;
350 test_data1->sleep = 500;
351 test_data1->no_clear_done = 1;
352 ast_mutex_init(&test_data1->lock);
353 ast_cond_init(&test_data1->cond, NULL);
354
355 ast_test_status_update(test, "This test will take about %3.1f seconds\n",
356 ((test_data1->interval * 4) + test_data1->sleep) / 1000.0);
357
358 task = ast_sip_schedule_task(NULL, test_data1->interval, task_1, "test_1", test_data1,
360 ast_test_validate(test, task != NULL);
361
362 waitfor(test_data1);
363 when = ast_tvdiff_ms(test_data1->task_start, test_data1->test_start);
364 ast_test_validate(test, when > test_data1->interval * 0.9 && when < test_data1->interval * 1.1);
365
366 waitfor(test_data1);
367 when = ast_tvdiff_ms(test_data1->task_start, test_data1->test_start);
368 ast_test_validate(test, when > test_data1->interval * 2 * 0.9 && when < test_data1->interval * 2 * 1.1);
369
370 waitfor(test_data1);
371 when = ast_tvdiff_ms(test_data1->task_start, test_data1->test_start);
372 ast_test_validate(test, when > test_data1->interval * 3 * 0.9 && when < test_data1->interval * 3 * 1.1);
373
375
376 /* Wait a full interval in case a 4th call to test_1 happened before the cancel */
377 usleep(M2U(test_data1->interval));
378
379 ast_mutex_lock(&test_data1->lock);
380 if (test_data1->done) {
381 int done = test_data1->done;
382
383 test_data1->done = 0;
384 ast_mutex_unlock(&test_data1->lock);
385
386 ast_test_validate(test, done == 1);
387
388 /* Wait two full intervals to be certain no further calls to test_1. */
389 usleep(M2U(test_data1->interval * 2));
390
391 ast_mutex_lock(&test_data1->lock);
392 if (test_data1->done != 0) {
393 ast_mutex_unlock(&test_data1->lock);
394 /* The cancelation failed so we need to prevent cleanup of
395 * test_data1 to prevent a crash from write-after-free. */
396 test_data1 = NULL;
397 ast_test_status_update(test, "Failed to cancel task");
398 return AST_TEST_FAIL;
399 }
400 }
401 ast_mutex_unlock(&test_data1->lock);
402
403 return AST_TEST_PASS;
404}
405
406static int load_module(void)
407{
408 AST_TEST_REGISTER(serialized_scheduler);
409 AST_TEST_REGISTER(unserialized_scheduler);
410 AST_TEST_REGISTER(scheduler_cleanup);
411 AST_TEST_REGISTER(scheduler_cancel);
412 AST_TEST_REGISTER(scheduler_policy);
414}
415
416static int unload_module(void)
417{
418 AST_TEST_UNREGISTER(scheduler_cancel);
419 AST_TEST_UNREGISTER(scheduler_cleanup);
420 AST_TEST_UNREGISTER(unserialized_scheduler);
421 AST_TEST_UNREGISTER(serialized_scheduler);
422 AST_TEST_UNREGISTER(scheduler_policy);
423 return 0;
424}
425
426AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "res_pjsip scheduler test module",
427 .support_level = AST_MODULE_SUPPORT_CORE,
428 .load = load_module,
429 .unload = unload_module,
430 .requires = "res_pjsip",
Asterisk main include file. File version handling, generic pbx functions.
#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
int ast_sip_sched_task_cancel_by_name(const char *name)
Cancels the next invocation of a task by name.
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition: res_pjsip.c:2094
struct ast_sip_sched_task * ast_sip_schedule_task(struct ast_taskprocessor *serializer, int interval, ast_sip_task sip_task, const char *name, void *task_data, enum ast_sip_scheduler_task_flags flags)
Schedule a task to run in the res_pjsip thread pool.
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd)
Cancels the next invocation of a task.
int ast_sip_sched_is_task_running(struct ast_sip_sched_task *schtd)
Checks if the task is currently running.
int ast_sip_thread_is_servant(void)
Determine if the current thread is a SIP servant thread.
Definition: res_pjsip.c:2310
int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd)
Gets the number of milliseconds until the next invocation.
int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd, struct timeval *when_queued, struct timeval *last_start, struct timeval *last_end)
Gets the last start and end times of the task.
int ast_sip_sched_task_get_next_run_by_name(const char *name)
Gets the number of milliseconds until the next invocation.
int ast_sip_sched_is_task_running_by_name(const char *name)
Checks if the task is currently running.
@ AST_SIP_SCHED_TASK_FIXED
Definition: res_pjsip.h:2073
@ AST_SIP_SCHED_TASK_PERIODIC
The task is scheduled at multiples of interval.
Definition: res_pjsip.h:2112
@ AST_SIP_SCHED_TASK_DATA_AO2
Definition: res_pjsip.h:2096
@ AST_SIP_SCHED_TASK_DATA_FREE
Definition: res_pjsip.h:2106
@ AST_SIP_SCHED_TASK_DATA_NO_CLEANUP
Definition: res_pjsip.h:2101
#define ast_cond_destroy(cond)
Definition: lock.h:202
#define ast_cond_init(cond, attr)
Definition: lock.h:201
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#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.
@ AST_MODFLAG_DEFAULT
Definition: module.h:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#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
Structure for mutex and tracking information.
Definition: lock.h:135
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
Sorcery object created based on backend data.
struct timeval task_end
struct ast_test * test
struct timeval task_start
struct timeval test_start
An API for managing task processing threads that can be shared across modules.
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
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_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
int done
Definition: test_amihooks.c:48
static int run_count
AST_TEST_DEFINE(serialized_scheduler)
static int task_1(void *data)
static int destruct_count
#define CATEGORY
static int dummy_task(void *data)
#define M2U(x)
static void test_destructor(void *data)
static void data_cleanup(void *data)
static int load_module(void)
static int unload_module(void)
#define waitfor(x)
static int scheduler(struct ast_test *test, int serialized)
static int task(void *data)
Queued task for baseline test.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int error(const char *format,...)
Definition: utils/frame.c:999
Utility functions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define MAX(a, b)
Definition: utils.h:233