Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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"
39 #include "asterisk/taskprocessor.h"
40 #include "asterisk/res_pjsip.h"
41 #include "asterisk/utils.h"
42 
43 #define CATEGORY "/res/res_pjsip/scheduler/"
44 
45 struct test_data {
48  pthread_t tid;
49  struct timeval test_start;
50  struct timeval task_start;
51  struct timeval task_end;
53  int interval;
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 
63 static 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();
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 
85 static void data_cleanup(void *data)
86 {
87  struct test_data *test_data = data;
88  ast_mutex_destroy(&test_data->lock);
89  ast_cond_destroy(&test_data->cond);
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 
102 static 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 
173 AST_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 
190 AST_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 
207 static int run_count;
208 static int destruct_count;
209 
210 static int dummy_task(void *data)
211 {
212  int *sleep = data;
213 
214  usleep(M2U(*sleep));
215  run_count++;
216 
217  return 0;
218 }
219 
220 static void test_destructor(void *data)
221 {
222  destruct_count++;
223 }
224 
225 AST_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 
276 AST_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 
326 AST_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 
406 static 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 
416 static 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 
426 AST_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",
431 );
#define waitfor(x)
struct timeval task_end
Asterisk main include file. File version handling, generic pbx functions.
The task is scheduled at multiples of interval.
Definition: res_pjsip.h:1817
static int load_module(void)
static int run_count
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd)
Cancels the next invocation of a task.
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_cond_init(cond, attr)
Definition: lock.h:199
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ast_mutex_lock(a)
Definition: lock.h:187
static int task_1(void *data)
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
#define NULL
Definition: resample.c:96
static void test_destructor(void *data)
#define ast_cond_signal(cond)
Definition: lock.h:201
struct timeval test_start
AST_TEST_DEFINE(serialized_scheduler)
int ast_sip_sched_is_task_running(struct ast_sip_sched_task *schtd)
Checks if the task is currently running.
static int task(void *data)
Queued task for baseline test.
Utility functions.
pthread_cond_t ast_cond_t
Definition: lock.h:176
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.
#define MAX(a, b)
Definition: utils.h:228
int ast_sip_sched_task_get_next_run_by_name(const char *name)
Gets the number of milliseconds until the next invocation.
#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:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
static int unload_module(void)
#define ao2_ref(o, delta)
Definition: astobj2.h:464
struct timeval task_start
static int scheduler(struct ast_test *test, int serialized)
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.
static int dummy_task(void *data)
struct ast_test * test
int ast_sip_sched_is_task_running_by_name(const char *name)
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:5321
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
Sorcery object created based on backend data.
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition: res_pjsip.c:5133
#define ast_cond_destroy(cond)
Definition: lock.h:200
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd)
Gets the number of milliseconds until the next invocation.
An API for managing task processing threads that can be shared across modules.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
Definition: test.c:65
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
#define M2U(x)
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
#define CATEGORY
int error(const char *format,...)
Definition: utils/frame.c:999
#define ast_mutex_init(pmutex)
Definition: lock.h:184
#define ast_mutex_destroy(a)
Definition: lock.h:186
static int destruct_count
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static void data_cleanup(void *data)
Structure for mutex and tracking information.
Definition: lock.h:135
int ast_sip_sched_task_cancel_by_name(const char *name)
Cancels the next invocation of a task by name.
#define ast_mutex_unlock(a)
Definition: lock.h:188