2
0

handle.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * Copyright (c) 2018-2023 SignalWire, Inc
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in all
  12. * copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. #include "KSTest.hpp"
  23. #include "libks/ks_atomic.h"
  24. #include "libks/internal/ks_handle.h"
  25. #include "catch/catch.hpp"
  26. #define KS_HANDLE_GROUP_TEST KS_HANDLE_USER_GROUP_START
  27. typedef enum {
  28. KS_HANDLE_TYPE_TEST_1 = KS_HANDLE_MAKE_TYPE(TEST, 1),
  29. KS_HANDLE_TYPE_TEST_2 = KS_HANDLE_MAKE_TYPE(TEST, 2),
  30. KS_HANDLE_TYPE_TEST_3 = KS_HANDLE_MAKE_TYPE(TEST, 3),
  31. KS_HANDLE_TYPE_TEST_4 = KS_HANDLE_MAKE_TYPE(TEST, 4),
  32. } swclt_handle_types;
  33. #define KS_HANDLE_TYPE_TEST KS_HANDLE_TYPE_TEST_1
  34. using namespace signalwire::pal;
  35. using namespace signalwire::pal::async;
  36. TEST_CASE("handle_dword_macros")
  37. {
  38. auto val = KS_HANDLE_MAKE_DWORD(0x1234, 0x5678);
  39. REQUIRE(val == 0x12345678);
  40. val = KS_HANDLE_MAKE_DWORD(0x5678, 0x1234);
  41. REQUIRE(val == 0x56781234);
  42. }
  43. TEST_CASE("handle_qword_macros")
  44. {
  45. auto val = KS_HANDLE_MAKE_QWORD(0x01234567, 0x89ABCDEF);
  46. REQUIRE(val == 0x0123456789ABCDEFull);
  47. val = KS_HANDLE_MAKE_QWORD(0x89ABCDEF, 0x01234567);
  48. REQUIRE(val == 0x89ABCDEF01234567ull);
  49. }
  50. TEST_CASE("handle_type_macros")
  51. {
  52. auto val = KS_HANDLE_MAKE_TYPE(TEST, 10);
  53. REQUIRE(KS_HANDLE_GROUP_FROM_TYPE(val) == KS_HANDLE_GROUP_TEST);
  54. REQUIRE(KS_HANDLE_GROUP_INDEX_FROM_TYPE(val) == 10);
  55. }
  56. TEST_CASE("handle_handle_macros")
  57. {
  58. auto val = KS_HANDLE_MAKE_HANDLE(KS_HANDLE_TYPE_TEST, 512, 8);
  59. REQUIRE(KS_HANDLE_GROUP_FROM_TYPE(KS_HANDLE_TYPE_TEST) == KS_HANDLE_GROUP_TEST);
  60. REQUIRE(KS_HANDLE_SLOT_INDEX_FROM_HANDLE(val) == 8);
  61. REQUIRE(KS_HANDLE_SLOT_SEQUENCE_FROM_HANDLE(val) == 512);
  62. REQUIRE(KS_HANDLE_GROUP_FROM_HANDLE(val) == KS_HANDLE_GROUP_TEST);
  63. REQUIRE(KS_HANDLE_GROUP_INDEX_FROM_HANDLE(val) == KS_HANDLE_GROUP_INDEX_FROM_TYPE(KS_HANDLE_TYPE_TEST));
  64. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(val) == KS_HANDLE_TYPE_TEST);
  65. }
  66. static void __fake_destroy(void *bobo)
  67. {
  68. }
  69. TEST_CASE("handle_enum")
  70. {
  71. void *handle_test_data_1;
  72. ks_handle_t handle_test_handle_1;
  73. void *handle_test_data_2;
  74. ks_handle_t handle_test_handle_2;
  75. void *handle_test_data_3;
  76. ks_handle_t handle_test_handle_3;
  77. void *handle_test_data_4_1;
  78. ks_handle_t handle_test_handle_4_1;
  79. void *handle_test_data_4_2;
  80. ks_handle_t handle_test_handle_4_2;
  81. ks_handle_alloc(KS_HANDLE_TYPE_TEST_1, sizeof(ks_handle_base_t), &handle_test_data_1, &handle_test_handle_1, __fake_destroy);
  82. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_1) == KS_HANDLE_TYPE_TEST_1);
  83. ks_handle_set_ready(handle_test_handle_1);
  84. ks_handle_alloc(KS_HANDLE_TYPE_TEST_2, sizeof(ks_handle_base_t), &handle_test_data_2, &handle_test_handle_2, __fake_destroy);
  85. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_2) == KS_HANDLE_TYPE_TEST_2);
  86. ks_handle_set_ready(handle_test_handle_2);
  87. ks_handle_alloc(KS_HANDLE_TYPE_TEST_3, sizeof(ks_handle_base_t), &handle_test_data_3, &handle_test_handle_3, __fake_destroy);
  88. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_3) == KS_HANDLE_TYPE_TEST_3);
  89. ks_handle_set_ready(handle_test_handle_3);
  90. ks_handle_alloc(KS_HANDLE_TYPE_TEST_4, sizeof(ks_handle_base_t), &handle_test_data_4_1, &handle_test_handle_4_1, __fake_destroy);
  91. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_4_1) == KS_HANDLE_TYPE_TEST_4);
  92. ks_handle_set_ready(handle_test_handle_4_1);
  93. ks_handle_alloc(KS_HANDLE_TYPE_TEST_4, sizeof(ks_handle_base_t), &handle_test_data_4_2, &handle_test_handle_4_2, __fake_destroy);
  94. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_4_2) == KS_HANDLE_TYPE_TEST_4);
  95. ks_handle_set_ready(handle_test_handle_4_2);
  96. ks_handle_t next = 0;
  97. uint32_t total_type_1 = 0, total_type_2 = 0, total_type_3 = 0, total_type_4_1 = 0, total_type_4_2 = 0;
  98. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_1, &next)) {
  99. REQUIRE(next == handle_test_handle_1);
  100. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_1);
  101. total_type_1++;
  102. }
  103. next = 0;
  104. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_2, &next)) {
  105. REQUIRE(next == handle_test_handle_2);
  106. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_2);
  107. total_type_2++;
  108. }
  109. next = 0;
  110. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_3, &next)) {
  111. REQUIRE(next == handle_test_handle_3);
  112. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_3);
  113. total_type_3++;
  114. }
  115. next = 0;
  116. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_4, &next)) {
  117. REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_4);
  118. if (next == handle_test_handle_4_2)
  119. total_type_4_2++;
  120. else if (next == handle_test_handle_4_1)
  121. total_type_4_1++;
  122. else
  123. PAL_FATALITYM("Unexpected handle:", next);
  124. }
  125. REQUIRE(total_type_1 == 1);
  126. REQUIRE(total_type_2 == 1);
  127. REQUIRE(total_type_3 == 1);
  128. REQUIRE(total_type_4_1 == 1);
  129. REQUIRE(total_type_4_2 == 1);
  130. ks_handle_destroy(&handle_test_handle_1);
  131. ks_handle_destroy(&handle_test_handle_2);
  132. ks_handle_destroy(&handle_test_handle_3);
  133. ks_handle_destroy(&handle_test_handle_4_1);
  134. ks_handle_destroy(&handle_test_handle_4_2);
  135. next = 0;
  136. total_type_1 = 0, total_type_2 = 0, total_type_3 = 0, total_type_4_1 = 0, total_type_4_2 = 0;
  137. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_1, &next)) {
  138. REQUIRE(!"Error, should not have enumerated test 1 handle");
  139. }
  140. next = 0;
  141. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_2, &next)) {
  142. REQUIRE(!"Error, should not have enumerated test 2 handle");
  143. }
  144. next = 0;
  145. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_3, &next)) {
  146. REQUIRE(!"Error, should not have enumerated test 3 handle");
  147. }
  148. next = 0;
  149. while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_4, &next)) {
  150. REQUIRE(!"Error, should not have enumerated test 4 handle");
  151. }
  152. REQUIRE(total_type_1 == 0);
  153. REQUIRE(total_type_2 == 0);
  154. REQUIRE(total_type_3 == 0);
  155. REQUIRE(total_type_4_1 == 0);
  156. REQUIRE(total_type_4_2 == 0);
  157. }
  158. static std::atomic<uint32_t> g_context_deinit_count = 0;
  159. static std::atomic<uint32_t> g_expected_deinit_count = 0;
  160. static void __context_deinit(void *data)
  161. {
  162. g_context_deinit_count++;
  163. }
  164. TEST_CASE("handle_basic_api")
  165. {
  166. auto testRun = [&]() {
  167. ks_handle_t hThread;
  168. ks_handle_base_t *thread = nullptr, *_thread = nullptr;
  169. auto checkref = [&](uint32_t ref) {
  170. uint32_t current_ref;
  171. KCHECK(ks_handle_refcount(hThread, &current_ref));
  172. PAL_ASSERT(ref == current_ref);
  173. };
  174. // Test alloc/ready
  175. KCHECK(ks_handle_alloc(KS_HANDLE_TYPE_TEST, 30, &thread, &hThread, __context_deinit));
  176. KCHECK(ks_handle_set_ready(hThread));
  177. // Test gets
  178. KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
  179. checkref(1);
  180. KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
  181. checkref(2);
  182. KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
  183. checkref(3);
  184. // Test puts
  185. _thread = thread;
  186. KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
  187. checkref(2);
  188. _thread = thread;
  189. KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
  190. checkref(1);
  191. _thread = thread;
  192. KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
  193. checkref(0);
  194. KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
  195. checkref(1);
  196. PAL_ASSERT(_thread == thread);
  197. KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
  198. checkref(0);
  199. ks_handle_destroy(&hThread);
  200. g_expected_deinit_count++;
  201. };
  202. std::vector<task::TaskPtr> tasks;
  203. for (auto i = 0; i < 50; i++) {
  204. LOG(task, "Spawning handle basic iteration:", i);
  205. tasks.emplace_back(task::start(string::toString("Handle basic test:", i), testRun));
  206. }
  207. for (auto &task : tasks) {
  208. LOG(task, "Joining on test task:", task->getName());
  209. task->join();
  210. }
  211. REQUIRE(g_context_deinit_count == g_expected_deinit_count);
  212. }
  213. TEST_CASE("handle_threaded_stress")
  214. {
  215. auto checkref = [&](ks_handle_t handle, uint32_t expectedRefCount) {
  216. uint32_t current_ref;
  217. if (!ks_handle_refcount(handle, &current_ref))
  218. REQUIRE(expectedRefCount == current_ref);
  219. };
  220. std::atomic<uint64_t> checkoutCount = {0};
  221. // This function just loops on a check out/check in until the check out fails
  222. auto checkouter = [&](ks_handle_t handle) {
  223. void *thread;
  224. forever {
  225. if (ks_handle_get(KS_HANDLE_TYPE_TEST, handle, &thread)) {
  226. return;
  227. }
  228. checkref(handle, 1);
  229. ks_handle_put(KS_HANDLE_TYPE_TEST, &thread);
  230. checkoutCount++;
  231. }
  232. };
  233. // Create a handle
  234. void *thread;
  235. ks_handle_t handle;
  236. KCHECK(ks_handle_alloc(KS_HANDLE_TYPE_TEST, sizeof(ks_handle_base_t), &thread, &handle, __context_deinit));
  237. KCHECK(ks_handle_set_ready(handle));
  238. // Kick off a thread that just checks out and checks in like crazy
  239. Thread checkoutThread("Checkouter", checkouter, handle);
  240. checkoutThread.start();
  241. // Let the checkout counts build up a bit
  242. while (checkoutCount < 100) {
  243. sleep(time::seconds(1));
  244. }
  245. // Now mark the handle not ready
  246. KCHECK(ks_handle_set_notready(KS_HANDLE_TYPE_TEST, handle, &thread));
  247. // The checkout count should be stopped now
  248. sleep(time::seconds(1));
  249. REQUIRE(checkoutThread.isStopped());
  250. // Should be able to destroy it now
  251. KCHECK(ks_handle_destroy(&handle));
  252. }