123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- /*
- * Copyright (c) 2018-2023 SignalWire, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- #include "KSTest.hpp"
- #include "libks/ks_atomic.h"
- #include "libks/internal/ks_handle.h"
- #include "catch/catch.hpp"
- #define KS_HANDLE_GROUP_TEST KS_HANDLE_USER_GROUP_START
- typedef enum {
- KS_HANDLE_TYPE_TEST_1 = KS_HANDLE_MAKE_TYPE(TEST, 1),
- KS_HANDLE_TYPE_TEST_2 = KS_HANDLE_MAKE_TYPE(TEST, 2),
- KS_HANDLE_TYPE_TEST_3 = KS_HANDLE_MAKE_TYPE(TEST, 3),
- KS_HANDLE_TYPE_TEST_4 = KS_HANDLE_MAKE_TYPE(TEST, 4),
- } swclt_handle_types;
- #define KS_HANDLE_TYPE_TEST KS_HANDLE_TYPE_TEST_1
- using namespace signalwire::pal;
- using namespace signalwire::pal::async;
- TEST_CASE("handle_dword_macros")
- {
- auto val = KS_HANDLE_MAKE_DWORD(0x1234, 0x5678);
- REQUIRE(val == 0x12345678);
- val = KS_HANDLE_MAKE_DWORD(0x5678, 0x1234);
- REQUIRE(val == 0x56781234);
- }
- TEST_CASE("handle_qword_macros")
- {
- auto val = KS_HANDLE_MAKE_QWORD(0x01234567, 0x89ABCDEF);
- REQUIRE(val == 0x0123456789ABCDEFull);
- val = KS_HANDLE_MAKE_QWORD(0x89ABCDEF, 0x01234567);
- REQUIRE(val == 0x89ABCDEF01234567ull);
- }
- TEST_CASE("handle_type_macros")
- {
- auto val = KS_HANDLE_MAKE_TYPE(TEST, 10);
- REQUIRE(KS_HANDLE_GROUP_FROM_TYPE(val) == KS_HANDLE_GROUP_TEST);
- REQUIRE(KS_HANDLE_GROUP_INDEX_FROM_TYPE(val) == 10);
- }
- TEST_CASE("handle_handle_macros")
- {
- auto val = KS_HANDLE_MAKE_HANDLE(KS_HANDLE_TYPE_TEST, 512, 8);
- REQUIRE(KS_HANDLE_GROUP_FROM_TYPE(KS_HANDLE_TYPE_TEST) == KS_HANDLE_GROUP_TEST);
- REQUIRE(KS_HANDLE_SLOT_INDEX_FROM_HANDLE(val) == 8);
- REQUIRE(KS_HANDLE_SLOT_SEQUENCE_FROM_HANDLE(val) == 512);
- REQUIRE(KS_HANDLE_GROUP_FROM_HANDLE(val) == KS_HANDLE_GROUP_TEST);
- REQUIRE(KS_HANDLE_GROUP_INDEX_FROM_HANDLE(val) == KS_HANDLE_GROUP_INDEX_FROM_TYPE(KS_HANDLE_TYPE_TEST));
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(val) == KS_HANDLE_TYPE_TEST);
- }
- static void __fake_destroy(void *bobo)
- {
- }
- TEST_CASE("handle_enum")
- {
- void *handle_test_data_1;
- ks_handle_t handle_test_handle_1;
- void *handle_test_data_2;
- ks_handle_t handle_test_handle_2;
- void *handle_test_data_3;
- ks_handle_t handle_test_handle_3;
- void *handle_test_data_4_1;
- ks_handle_t handle_test_handle_4_1;
- void *handle_test_data_4_2;
- ks_handle_t handle_test_handle_4_2;
- ks_handle_alloc(KS_HANDLE_TYPE_TEST_1, sizeof(ks_handle_base_t), &handle_test_data_1, &handle_test_handle_1, __fake_destroy);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_1) == KS_HANDLE_TYPE_TEST_1);
- ks_handle_set_ready(handle_test_handle_1);
- ks_handle_alloc(KS_HANDLE_TYPE_TEST_2, sizeof(ks_handle_base_t), &handle_test_data_2, &handle_test_handle_2, __fake_destroy);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_2) == KS_HANDLE_TYPE_TEST_2);
- ks_handle_set_ready(handle_test_handle_2);
- ks_handle_alloc(KS_HANDLE_TYPE_TEST_3, sizeof(ks_handle_base_t), &handle_test_data_3, &handle_test_handle_3, __fake_destroy);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_3) == KS_HANDLE_TYPE_TEST_3);
- ks_handle_set_ready(handle_test_handle_3);
- 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);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_4_1) == KS_HANDLE_TYPE_TEST_4);
- ks_handle_set_ready(handle_test_handle_4_1);
- 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);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(handle_test_handle_4_2) == KS_HANDLE_TYPE_TEST_4);
- ks_handle_set_ready(handle_test_handle_4_2);
- ks_handle_t next = 0;
- uint32_t total_type_1 = 0, total_type_2 = 0, total_type_3 = 0, total_type_4_1 = 0, total_type_4_2 = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_1, &next)) {
- REQUIRE(next == handle_test_handle_1);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_1);
- total_type_1++;
- }
- next = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_2, &next)) {
- REQUIRE(next == handle_test_handle_2);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_2);
- total_type_2++;
- }
- next = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_3, &next)) {
- REQUIRE(next == handle_test_handle_3);
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_3);
- total_type_3++;
- }
- next = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_4, &next)) {
- REQUIRE(KS_HANDLE_TYPE_FROM_HANDLE(next) == KS_HANDLE_TYPE_TEST_4);
- if (next == handle_test_handle_4_2)
- total_type_4_2++;
- else if (next == handle_test_handle_4_1)
- total_type_4_1++;
- else
- PAL_FATALITYM("Unexpected handle:", next);
- }
- REQUIRE(total_type_1 == 1);
- REQUIRE(total_type_2 == 1);
- REQUIRE(total_type_3 == 1);
- REQUIRE(total_type_4_1 == 1);
- REQUIRE(total_type_4_2 == 1);
- ks_handle_destroy(&handle_test_handle_1);
- ks_handle_destroy(&handle_test_handle_2);
- ks_handle_destroy(&handle_test_handle_3);
- ks_handle_destroy(&handle_test_handle_4_1);
- ks_handle_destroy(&handle_test_handle_4_2);
- next = 0;
- total_type_1 = 0, total_type_2 = 0, total_type_3 = 0, total_type_4_1 = 0, total_type_4_2 = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_1, &next)) {
- REQUIRE(!"Error, should not have enumerated test 1 handle");
- }
- next = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_2, &next)) {
- REQUIRE(!"Error, should not have enumerated test 2 handle");
- }
- next = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_3, &next)) {
- REQUIRE(!"Error, should not have enumerated test 3 handle");
- }
- next = 0;
- while (!ks_handle_enum_type(KS_HANDLE_TYPE_TEST_4, &next)) {
- REQUIRE(!"Error, should not have enumerated test 4 handle");
- }
- REQUIRE(total_type_1 == 0);
- REQUIRE(total_type_2 == 0);
- REQUIRE(total_type_3 == 0);
- REQUIRE(total_type_4_1 == 0);
- REQUIRE(total_type_4_2 == 0);
- }
- static std::atomic<uint32_t> g_context_deinit_count = 0;
- static std::atomic<uint32_t> g_expected_deinit_count = 0;
- static void __context_deinit(void *data)
- {
- g_context_deinit_count++;
- }
- TEST_CASE("handle_basic_api")
- {
- auto testRun = [&]() {
- ks_handle_t hThread;
- ks_handle_base_t *thread = nullptr, *_thread = nullptr;
- auto checkref = [&](uint32_t ref) {
- uint32_t current_ref;
- KCHECK(ks_handle_refcount(hThread, ¤t_ref));
- PAL_ASSERT(ref == current_ref);
- };
- // Test alloc/ready
- KCHECK(ks_handle_alloc(KS_HANDLE_TYPE_TEST, 30, &thread, &hThread, __context_deinit));
- KCHECK(ks_handle_set_ready(hThread));
- // Test gets
- KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
- checkref(1);
- KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
- checkref(2);
- KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
- checkref(3);
- // Test puts
- _thread = thread;
- KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
- checkref(2);
- _thread = thread;
- KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
- checkref(1);
- _thread = thread;
- KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
- checkref(0);
- KCHECK(ks_handle_get(KS_HANDLE_TYPE_TEST, hThread, &_thread));
- checkref(1);
- PAL_ASSERT(_thread == thread);
- KCHECK(ks_handle_put(KS_HANDLE_TYPE_TEST, &_thread));
- checkref(0);
- ks_handle_destroy(&hThread);
- g_expected_deinit_count++;
- };
- std::vector<task::TaskPtr> tasks;
- for (auto i = 0; i < 50; i++) {
- LOG(task, "Spawning handle basic iteration:", i);
- tasks.emplace_back(task::start(string::toString("Handle basic test:", i), testRun));
- }
- for (auto &task : tasks) {
- LOG(task, "Joining on test task:", task->getName());
- task->join();
- }
- REQUIRE(g_context_deinit_count == g_expected_deinit_count);
- }
- TEST_CASE("handle_threaded_stress")
- {
- auto checkref = [&](ks_handle_t handle, uint32_t expectedRefCount) {
- uint32_t current_ref;
- if (!ks_handle_refcount(handle, ¤t_ref))
- REQUIRE(expectedRefCount == current_ref);
- };
- std::atomic<uint64_t> checkoutCount = {0};
- // This function just loops on a check out/check in until the check out fails
- auto checkouter = [&](ks_handle_t handle) {
- void *thread;
- forever {
- if (ks_handle_get(KS_HANDLE_TYPE_TEST, handle, &thread)) {
- return;
- }
- checkref(handle, 1);
- ks_handle_put(KS_HANDLE_TYPE_TEST, &thread);
- checkoutCount++;
- }
- };
- // Create a handle
- void *thread;
- ks_handle_t handle;
- KCHECK(ks_handle_alloc(KS_HANDLE_TYPE_TEST, sizeof(ks_handle_base_t), &thread, &handle, __context_deinit));
- KCHECK(ks_handle_set_ready(handle));
- // Kick off a thread that just checks out and checks in like crazy
- Thread checkoutThread("Checkouter", checkouter, handle);
- checkoutThread.start();
- // Let the checkout counts build up a bit
- while (checkoutCount < 100) {
- sleep(time::seconds(1));
- }
- // Now mark the handle not ready
- KCHECK(ks_handle_set_notready(KS_HANDLE_TYPE_TEST, handle, &thread));
- // The checkout count should be stopped now
- sleep(time::seconds(1));
- REQUIRE(checkoutThread.isStopped());
- // Should be able to destroy it now
- KCHECK(ks_handle_destroy(&handle));
- }
|