Asterisk - The Open Source Telephony Project  18.5.0
test_scoped_lock.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012, Digium, Inc.
5  *
6  * Mark Michelson <[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 SCOPED_LOCK unit tests
22  *
23  * \author Mark Michelson <[email protected]>
24  *
25  */
26 
27 /*** MODULEINFO
28  <depend>TEST_FRAMEWORK</depend>
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/test.h"
35 #include "asterisk/utils.h"
36 #include "asterisk/module.h"
37 #include "asterisk/astobj2.h"
38 
39 static int indicator;
40 static struct ast_test *current_test;
42 
43 static void lock_it(ast_mutex_t *lock)
44 {
45  indicator = 1;
46  ast_mutex_lock(lock);
47 }
48 
49 static void unlock_it(ast_mutex_t *lock)
50 {
51  indicator = 0;
52  ast_mutex_unlock(lock);
53 }
54 
55 AST_TEST_DEFINE(lock_test)
56 {
58  int i;
59 
60  switch(cmd) {
61  case TEST_INIT:
62  info->name = "lock_test";
63  info->category = "/main/lock/";
64  info->summary = "SCOPED_LOCK test";
65  info->description =
66  "Tests that scoped locks are scoped as they are expected to be";
67  return AST_TEST_NOT_RUN;
68  case TEST_EXECUTE:
69  break;
70  }
71 
72  current_test = test;
73  indicator = 0;
74  {
76  if (indicator != 1) {
77  ast_log(LOG_ERROR, "The lock was not acquired via RAII");
78  res = AST_TEST_FAIL;
79  }
80  }
81  if (indicator != 0) {
82  ast_log(LOG_ERROR, "The lock was not released when the variable went out of scope");
83  res = AST_TEST_FAIL;
84  }
85 
86  for (i = 0; i < 10; ++i) {
88  if (indicator != 1) {
89  ast_log(LOG_ERROR, "The lock was not acquired via RAII");
90  res = AST_TEST_FAIL;
91  }
92  }
93 
94  if (indicator != 0) {
95  ast_log(LOG_ERROR, "The lock was not released when the variable went out of scope");
96  res = AST_TEST_FAIL;
97  }
98 
99  return res;
100 }
101 
103 {
104  int locked;
105  int reffed;
106 };
107 
108 /*!
109  * \brief lock callback function
110  *
111  * Locks the object passed in. Only sets the locked
112  * flag if the object is reffed. This allows us to check
113  * that locking is always occurring after reffing.
114  */
115 static void test_lock(struct test_struct *test)
116 {
117  ast_test_status_update(current_test, "Lock is occurring\n");
118  ao2_lock(test);
119  if (test->reffed) {
120  test->locked = 1;
121  }
122 }
123 
124 /*!
125  * \brief unlock callback function
126  *
127  * Unlocks the object passed in. Only clears the locked
128  * flag if the object is still reffed. This allows us to
129  * ensure that unlocking is always occurring before unreffing.
130  */
131 static void test_unlock(struct test_struct *test)
132 {
133  ast_test_status_update(current_test, "Unlock is occurring\n");
134  ao2_unlock(test);
135  if (test->reffed) {
136  test->locked = 0;
137  }
138 }
139 
140 /*!
141  * \brief ref callback function
142  *
143  * Refs the object passed in. Only sets the reffed flag if
144  * the object is not locked. This allows us to ensure that
145  * reffing always occurs before locking.
146  */
147 static struct test_struct *test_ref(struct test_struct *test)
148 {
149  ast_test_status_update(current_test, "Ref is occurring\n");
150  ao2_ref(test, +1);
151  if (!test->locked) {
152  test->reffed = 1;
153  }
154  return test;
155 }
156 
157 /*!
158  * \brief unref callback function
159  *
160  * Unrefs the object passed in. Only sets the unreffed flag if
161  * the object is not locked. This allows us to ensure that
162  * unreffing always occurs after unlocking.
163  */
164 static void test_unref(struct test_struct *test)
165 {
166  ast_test_status_update(current_test, "Unref is occurring\n");
167  ao2_ref(test, -1);
168  if (!test->locked) {
169  test->reffed = 0;
170  }
171 }
172 
173 /*!
174  * \brief wrapper for ao2_iterator_next
175  *
176  * Grabs the next item in the container and replaces the ref acquired
177  * from ao2_iterator_next() with a call to test_ref().
178  */
179 static struct test_struct *test_iterator_next(struct ao2_iterator *iter)
180 {
181  struct test_struct *test = ao2_iterator_next(iter);
182 
183  if (!test) {
184  return NULL;
185  }
186 
187  /* Remove ref from ao2_iterator_next() and replace it with
188  * a test_ref() call. The order here is safe since we can guarantee
189  * the container still has a ref to the test structure.
190  */
191  ao2_ref(test, -1);
192  test_ref(test);
193 
194  return test;
195 }
196 
197 AST_TEST_DEFINE(cleanup_order)
198 {
200  struct ao2_iterator iter;
201  struct test_struct *object_iter;
203  RAII_VAR(struct test_struct *, object, NULL, ao2_cleanup);
204 
205  switch(cmd) {
206  case TEST_INIT:
207  info->name = "cleanup_order_test";
208  info->category = "/main/lock/";
209  info->summary = "cleanup order test";
210  info->description =
211  "Tests that variables with cleanup attributes are cleaned up\n"
212  "in the reverse order they are declared.";
213  return AST_TEST_NOT_RUN;
214  case TEST_EXECUTE:
215  break;
216  }
217  current_test = test;
218 
220  object = ao2_alloc(sizeof(*object), NULL);
221  if (!object || !container) {
222  /* Allocation failure. We can't even pretend to do this test properly */
223  return AST_TEST_FAIL;
224  }
225 
226  {
227  /* Purpose of this block is to make sure that the cleanup operations
228  * run in the reverse order that they were created here.
229  */
230  RAII_VAR(struct test_struct *, object2, test_ref(object), test_unref);
232  if (!object->reffed || !object->locked) {
233  ast_log(LOG_ERROR, "Test failed due to out of order initializations");
234  res = AST_TEST_FAIL;
235  }
236  }
237 
238  if (object->reffed || object->locked) {
239  ast_log(LOG_ERROR, "Test failed due to out of order cleanups\n");
240  res = AST_TEST_FAIL;
241  }
242 
243  /* Now link the object into the container for a little experiment ... */
244  ao2_link(container, object);
245 
246  /* This loop is to ensure that unrefs in a for loop occur after the cleanup
247  * operations of items inside the loop. If we hope to be able to mix scoped locks
248  * and ao2 refs, this is the way to go about it.
249  */
250  for (iter = ao2_iterator_init(container, 0);
251  (object_iter = test_iterator_next(&iter));
252  test_unref(object_iter)) {
253  SCOPED_LOCK(lock, object_iter, test_lock, test_unlock);
254  if (!object->reffed || !object->locked) {
255  ast_log(LOG_ERROR, "Test failed due to out of order initializations");
256  res = AST_TEST_FAIL;
257  }
258  }
259  ao2_iterator_destroy(&iter);
260 
261  if (object->reffed || object->locked) {
262  ast_log(LOG_ERROR, "Test failed due to out of order cleanups\n");
263  res = AST_TEST_FAIL;
264  }
265 
266  return res;
267 }
268 
269 static int unload_module(void)
270 {
271  AST_TEST_UNREGISTER(lock_test);
272  AST_TEST_UNREGISTER(cleanup_order);
273  return 0;
274 }
275 
276 static int load_module(void)
277 {
278  AST_TEST_REGISTER(lock_test);
279  AST_TEST_REGISTER(cleanup_order);
281 }
282 
283 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SCOPED_LOCK test module");
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
static ast_mutex_t the_lock
Asterisk main include file. File version handling, generic pbx functions.
static void test_lock(struct test_struct *test)
lock callback function
static int indicator
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ast_mutex_lock(a)
Definition: lock.h:187
#define ao2_unlock(a)
Definition: astobj2.h:730
static int unload_module(void)
#define NULL
Definition: resample.c:96
static struct test_struct * test_ref(struct test_struct *test)
ref callback function
static void lock_it(ast_mutex_t *lock)
Utility functions.
#define ast_log
Definition: astobj2.c:42
#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
ast_mutex_t lock
Definition: app_meetme.c:1091
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_lock(a)
Definition: astobj2.h:718
#define SCOPED_LOCK(varname, lock, lockfunc, unlockfunc)
Scoped Locks.
Definition: lock.h:581
struct ao2_container * container
Definition: res_fax.c:502
#define LOG_ERROR
Definition: logger.h:285
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
static void test_unlock(struct test_struct *test)
unlock callback function
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
static void test_unref(struct test_struct *test)
unref callback function
def info(msg)
static int load_module(void)
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static struct test_struct * test_iterator_next(struct ao2_iterator *iter)
wrapper for ao2_iterator_next
Definition: test.c:65
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
AST_TEST_DEFINE(lock_test)
Generic container type.
static void unlock_it(ast_mutex_t *lock)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:518
Structure for mutex and tracking information.
Definition: lock.h:135
ast_test_result_state
Definition: test.h:200
static struct ast_test * current_test
static struct ast_test * test
Definition: localtime.c:115
#define ast_mutex_unlock(a)
Definition: lock.h:188
#define ao2_link(container, obj)
Definition: astobj2.h:1549