154 lines
3.6 KiB
C++
154 lines
3.6 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
#include <utcommon.h>
|
|
|
|
#include <k4ainternal/global.h>
|
|
#include <k4ainternal/rwlock.h>
|
|
#include <k4ainternal/common.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <azure_c_shared_utility/threadapi.h>
|
|
#include <azure_c_shared_utility/refcount.h>
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
return k4a_test_common_main(argc, argv);
|
|
}
|
|
|
|
#define GTEST_LOG_INFO std::cout << "[ INFO ] "
|
|
|
|
static int g_GlobalCounter1 = 0;
|
|
|
|
typedef struct
|
|
{
|
|
int valueToInit;
|
|
} test_global_t;
|
|
|
|
static void globalInitFunction(test_global_t *global)
|
|
{
|
|
ASSERT_EQ(0, global->valueToInit);
|
|
ASSERT_EQ(0, g_GlobalCounter1);
|
|
|
|
g_GlobalCounter1++;
|
|
global->valueToInit = 1;
|
|
|
|
// Sleep to simulate an init function that takes some time
|
|
ThreadAPI_Sleep(50);
|
|
}
|
|
|
|
K4A_DECLARE_GLOBAL(test_global_t, globalInitFunction);
|
|
|
|
static volatile long g_GlobalCounter2 = 0;
|
|
|
|
typedef struct
|
|
{
|
|
int value1;
|
|
int value2;
|
|
} test_global2_t;
|
|
|
|
static void globalInitFunction2(test_global2_t *global)
|
|
{
|
|
ASSERT_EQ(0, global->value1);
|
|
ASSERT_EQ(0, global->value2);
|
|
ASSERT_EQ(0, g_GlobalCounter2);
|
|
|
|
ASSERT_EQ(1, INC_REF_VAR(g_GlobalCounter2));
|
|
|
|
global->value1 = 1;
|
|
|
|
// Sleep to simulate an init function that takes some time
|
|
ThreadAPI_Sleep(100);
|
|
global->value2 = 1;
|
|
|
|
ASSERT_EQ(1, global->value1);
|
|
ASSERT_EQ(1, global->value2);
|
|
}
|
|
|
|
K4A_DECLARE_GLOBAL(test_global2_t, globalInitFunction2);
|
|
|
|
TEST(global_ft, global_init_singlethread)
|
|
{
|
|
// We should start uninitialized
|
|
ASSERT_EQ(0, g_GlobalCounter1);
|
|
|
|
// Get the global context, and verify that it is initalized
|
|
test_global_t *g_test = test_global_t_get();
|
|
|
|
ASSERT_EQ(1, g_GlobalCounter1);
|
|
ASSERT_EQ(1, g_test->valueToInit);
|
|
|
|
// Get it again and verify initialization hasn't been run more than once
|
|
test_global_t *g_test2 = test_global_t_get();
|
|
|
|
ASSERT_EQ(1, g_GlobalCounter1);
|
|
ASSERT_EQ(1, g_test2->valueToInit);
|
|
|
|
// The global is a singleton
|
|
ASSERT_EQ(g_test, g_test2);
|
|
}
|
|
|
|
static void thread_testinit(k4a_rwlock_t *lock)
|
|
{
|
|
ASSERT_EQ(0, g_GlobalCounter2);
|
|
|
|
// Block on the lock to attempt simultaneous release of threads
|
|
rwlock_acquire_read(lock);
|
|
|
|
test_global2_t *g_test = test_global2_t_get();
|
|
|
|
ASSERT_EQ(1, g_GlobalCounter2);
|
|
ASSERT_EQ(1, g_test->value1);
|
|
ASSERT_EQ(1, g_test->value2);
|
|
|
|
rwlock_release_read(lock);
|
|
}
|
|
|
|
static int thread_testinit_threadproc(void *ctx)
|
|
{
|
|
thread_testinit((k4a_rwlock_t *)ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define THREAD_COUNT 10
|
|
TEST(global_ft, global_init_multithread)
|
|
{
|
|
THREAD_HANDLE thread[THREAD_COUNT];
|
|
// Try spinning up a series of threads to simultaneously try initialization
|
|
|
|
k4a_rwlock_t lock;
|
|
rwlock_init(&lock);
|
|
|
|
rwlock_acquire_write(&lock);
|
|
|
|
// Create the threads
|
|
for (int i = 0; i < THREAD_COUNT; i++)
|
|
{
|
|
ASSERT_EQ(THREADAPI_OK, ThreadAPI_Create(&thread[i], thread_testinit_threadproc, &lock));
|
|
GTEST_LOG_INFO << "Created thread " << i << " (" << thread[i] << ")" << std::endl;
|
|
}
|
|
|
|
// Wait for the threads to block
|
|
ThreadAPI_Sleep(200);
|
|
|
|
// Allow the threads to run all at once
|
|
rwlock_release_write(&lock);
|
|
|
|
// Wait for the threads to complete
|
|
for (int i = 0; i < THREAD_COUNT; i++)
|
|
{
|
|
GTEST_LOG_INFO << "Waiting on thread " << i << std::endl;
|
|
ThreadAPI_Join(thread[i], NULL);
|
|
}
|
|
|
|
rwlock_deinit(&lock);
|
|
|
|
// Verify initialization happened once
|
|
test_global2_t *g_test = test_global2_t_get();
|
|
|
|
ASSERT_EQ(1, g_GlobalCounter2);
|
|
ASSERT_EQ(1, g_test->value1);
|
|
ASSERT_EQ(1, g_test->value2);
|
|
}
|