Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 <mmichelson@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/*!
20 * \file
21 * \brief SCOPED_LOCK unit tests
22 *
23 * \author Mark Michelson <mmichelson@digium.com>
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
39static int indicator;
40static struct ast_test *current_test;
42
44{
45 indicator = 1;
47}
48
50{
51 indicator = 0;
53}
54
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
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{
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 */
115static 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 */
131static void test_unlock(struct test_struct *test)
132{
133 ast_test_status_update(current_test, "Unlock is occurring\n");
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 */
147static 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 */
164static 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 */
179static 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
197AST_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 }
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 }
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
269static int unload_module(void)
270{
271 AST_TEST_UNREGISTER(lock_test);
272 AST_TEST_UNREGISTER(cleanup_order);
273 return 0;
274}
275
276static int load_module(void)
277{
278 AST_TEST_REGISTER(lock_test);
279 AST_TEST_REGISTER(cleanup_order);
281}
282
283AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SCOPED_LOCK test module");
ast_mutex_t lock
Definition: app_sla.c:331
Asterisk main include file. File version handling, generic pbx functions.
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
#define LOG_ERROR
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define SCOPED_LOCK(varname, lock, lockfunc, unlockfunc)
Scoped Locks.
Definition: lock.h:583
#define ast_mutex_lock(a)
Definition: lock.h:189
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:520
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)
struct ao2_container * container
Definition: res_fax.c:501
#define NULL
Definition: resample.c:96
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Structure for mutex and tracking information.
Definition: lock.h:135
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_struct * test_ref(struct test_struct *test)
ref callback function
static void test_unref(struct test_struct *test)
unref callback function
static ast_mutex_t the_lock
static void test_unlock(struct test_struct *test)
unlock callback function
static struct ast_test * current_test
static void lock_it(ast_mutex_t *lock)
static void test_lock(struct test_struct *test)
lock callback function
AST_TEST_DEFINE(lock_test)
static int load_module(void)
static int unload_module(void)
static void unlock_it(ast_mutex_t *lock)
static struct test_struct * test_iterator_next(struct ao2_iterator *iter)
wrapper for ao2_iterator_next
static int indicator
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