test_scoped_lock.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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. * \file
  20. * \brief SCOPED_LOCK unit tests
  21. *
  22. * \author Mark Michelson <mmichelson@digium.com>
  23. *
  24. */
  25. /*** MODULEINFO
  26. <depend>TEST_FRAMEWORK</depend>
  27. <support_level>core</support_level>
  28. ***/
  29. #include "asterisk.h"
  30. #include "asterisk/test.h"
  31. #include "asterisk/utils.h"
  32. #include "asterisk/module.h"
  33. #include "asterisk/astobj2.h"
  34. static int indicator;
  35. static struct ast_test *current_test;
  36. AST_MUTEX_DEFINE_STATIC(the_lock);
  37. static void lock_it(ast_mutex_t *lock)
  38. {
  39. indicator = 1;
  40. ast_mutex_lock(lock);
  41. }
  42. static void unlock_it(ast_mutex_t *lock)
  43. {
  44. indicator = 0;
  45. ast_mutex_unlock(lock);
  46. }
  47. AST_TEST_DEFINE(lock_test)
  48. {
  49. enum ast_test_result_state res = AST_TEST_PASS;
  50. int i;
  51. switch(cmd) {
  52. case TEST_INIT:
  53. info->name = "lock_test";
  54. info->category = "/main/lock/";
  55. info->summary = "SCOPED_LOCK test";
  56. info->description =
  57. "Tests that scoped locks are scoped as they are expected to be";
  58. return AST_TEST_NOT_RUN;
  59. case TEST_EXECUTE:
  60. break;
  61. }
  62. current_test = test;
  63. indicator = 0;
  64. {
  65. SCOPED_LOCK(lock, &the_lock, lock_it, unlock_it);
  66. if (indicator != 1) {
  67. ast_log(LOG_ERROR, "The lock was not acquired via RAII");
  68. res = AST_TEST_FAIL;
  69. }
  70. }
  71. if (indicator != 0) {
  72. ast_log(LOG_ERROR, "The lock was not released when the variable went out of scope");
  73. res = AST_TEST_FAIL;
  74. }
  75. for (i = 0; i < 10; ++i) {
  76. SCOPED_LOCK(lock, &the_lock, lock_it, unlock_it);
  77. if (indicator != 1) {
  78. ast_log(LOG_ERROR, "The lock was not acquired via RAII");
  79. res = AST_TEST_FAIL;
  80. }
  81. }
  82. if (indicator != 0) {
  83. ast_log(LOG_ERROR, "The lock was not released when the variable went out of scope");
  84. res = AST_TEST_FAIL;
  85. }
  86. return res;
  87. }
  88. struct test_struct
  89. {
  90. int locked;
  91. int reffed;
  92. };
  93. /*!
  94. * \brief lock callback function
  95. *
  96. * Locks the object passed in. Only sets the locked
  97. * flag if the object is reffed. This allows us to check
  98. * that locking is always occurring after reffing.
  99. */
  100. static void test_lock(struct test_struct *test)
  101. {
  102. ast_test_status_update(current_test, "Lock is occurring\n");
  103. ao2_lock(test);
  104. if (test->reffed) {
  105. test->locked = 1;
  106. }
  107. }
  108. /*!
  109. * \brief unlock callback function
  110. *
  111. * Unlocks the object passed in. Only clears the locked
  112. * flag if the object is still reffed. This allows us to
  113. * ensure that unlocking is always occurring before unreffing.
  114. */
  115. static void test_unlock(struct test_struct *test)
  116. {
  117. ast_test_status_update(current_test, "Unlock is occurring\n");
  118. ao2_unlock(test);
  119. if (test->reffed) {
  120. test->locked = 0;
  121. }
  122. }
  123. /*!
  124. * \brief ref callback function
  125. *
  126. * Refs the object passed in. Only sets the reffed flag if
  127. * the object is not locked. This allows us to ensure that
  128. * reffing always occurs before locking.
  129. */
  130. static struct test_struct *test_ref(struct test_struct *test)
  131. {
  132. ast_test_status_update(current_test, "Ref is occurring\n");
  133. ao2_ref(test, +1);
  134. if (!test->locked) {
  135. test->reffed = 1;
  136. }
  137. return test;
  138. }
  139. /*!
  140. * \brief unref callback function
  141. *
  142. * Unrefs the object passed in. Only sets the unreffed flag if
  143. * the object is not locked. This allows us to ensure that
  144. * unreffing always occurs after unlocking.
  145. */
  146. static void test_unref(struct test_struct *test)
  147. {
  148. ast_test_status_update(current_test, "Unref is occurring\n");
  149. ao2_ref(test, -1);
  150. if (!test->locked) {
  151. test->reffed = 0;
  152. }
  153. }
  154. /*!
  155. * \brief wrapper for ao2_iterator_next
  156. *
  157. * Grabs the next item in the container and replaces the ref acquired
  158. * from ao2_iterator_next() with a call to test_ref().
  159. */
  160. static struct test_struct *test_iterator_next(struct ao2_iterator *iter)
  161. {
  162. struct test_struct *test = ao2_iterator_next(iter);
  163. if (!test) {
  164. return NULL;
  165. }
  166. /* Remove ref from ao2_iterator_next() and replace it with
  167. * a test_ref() call. The order here is safe since we can guarantee
  168. * the container still has a ref to the test structure.
  169. */
  170. ao2_ref(test, -1);
  171. test_ref(test);
  172. return test;
  173. }
  174. AST_TEST_DEFINE(cleanup_order)
  175. {
  176. enum ast_test_result_state res = AST_TEST_PASS;
  177. struct ao2_iterator iter;
  178. struct test_struct *object_iter;
  179. RAII_VAR(struct ao2_container*, container, NULL, ao2_cleanup);
  180. RAII_VAR(struct test_struct *, object, NULL, ao2_cleanup);
  181. switch(cmd) {
  182. case TEST_INIT:
  183. info->name = "cleanup_order_test";
  184. info->category = "/main/lock/";
  185. info->summary = "cleanup order test";
  186. info->description =
  187. "Tests that variables with cleanup attributes are cleaned up\n"
  188. "in the reverse order they are declared.";
  189. return AST_TEST_NOT_RUN;
  190. case TEST_EXECUTE:
  191. break;
  192. }
  193. current_test = test;
  194. container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 13, NULL, NULL, NULL);
  195. object = ao2_alloc(sizeof(*object), NULL);
  196. if (!object || !container) {
  197. /* Allocation failure. We can't even pretend to do this test properly */
  198. return AST_TEST_FAIL;
  199. }
  200. {
  201. /* Purpose of this block is to make sure that the cleanup operations
  202. * run in the reverse order that they were created here.
  203. */
  204. RAII_VAR(struct test_struct *, object2, test_ref(object), test_unref);
  205. SCOPED_LOCK(lock, object, test_lock, test_unlock);
  206. if (!object->reffed || !object->locked) {
  207. ast_log(LOG_ERROR, "Test failed due to out of order initializations");
  208. res = AST_TEST_FAIL;
  209. }
  210. }
  211. if (object->reffed || object->locked) {
  212. ast_log(LOG_ERROR, "Test failed due to out of order cleanups\n");
  213. res = AST_TEST_FAIL;
  214. }
  215. /* Now link the object into the container for a little experiment ... */
  216. ao2_link(container, object);
  217. /* This loop is to ensure that unrefs in a for loop occur after the cleanup
  218. * operations of items inside the loop. If we hope to be able to mix scoped locks
  219. * and ao2 refs, this is the way to go about it.
  220. */
  221. for (iter = ao2_iterator_init(container, 0);
  222. (object_iter = test_iterator_next(&iter));
  223. test_unref(object_iter)) {
  224. SCOPED_LOCK(lock, object_iter, test_lock, test_unlock);
  225. if (!object->reffed || !object->locked) {
  226. ast_log(LOG_ERROR, "Test failed due to out of order initializations");
  227. res = AST_TEST_FAIL;
  228. }
  229. }
  230. ao2_iterator_destroy(&iter);
  231. if (object->reffed || object->locked) {
  232. ast_log(LOG_ERROR, "Test failed due to out of order cleanups\n");
  233. res = AST_TEST_FAIL;
  234. }
  235. return res;
  236. }
  237. static int unload_module(void)
  238. {
  239. AST_TEST_UNREGISTER(lock_test);
  240. AST_TEST_UNREGISTER(cleanup_order);
  241. return 0;
  242. }
  243. static int load_module(void)
  244. {
  245. AST_TEST_REGISTER(lock_test);
  246. AST_TEST_REGISTER(cleanup_order);
  247. return AST_MODULE_LOAD_SUCCESS;
  248. }
  249. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SCOPED_LOCK test module");