Skip to content
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 161 additions & 1 deletion src/lock/tests/LockManagerTest.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
#include "firebird.h"
#include "boost/test/unit_test.hpp"
#include <atomic>
#include <chrono>
#include <functional>
#include <latch>
#include <memory>
#include <memory>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second include of the same file

#include <mutex>
#include <optional>
#include <unordered_map>
#include <thread>
#include <vector>
#include "../common/status.h"
Expand All @@ -19,6 +25,12 @@ namespace
{
class LockManagerTestCallbacks : public LockManager::Callbacks
{
public:
LockManagerTestCallbacks(std::mutex* aMutex = nullptr)
: mutex(aMutex)
{
}

public:
ISC_STATUS getCancelState() const
{
Expand All @@ -32,8 +44,18 @@ namespace

void checkoutRun(std::function<void()> func) const
{
func();
if (mutex)
{
Cleanup lockCleanup([&]() { mutex->lock(); });
mutex->unlock();
func();
}
else
func();
}

private:
std::mutex* mutex;
};
}

Expand Down Expand Up @@ -173,6 +195,144 @@ BOOST_AUTO_TEST_CASE(LockUnlockNoWaitTest)
}


BOOST_AUTO_TEST_CASE(LockUnlockAstTest)
{
struct Lock;

struct ThreadData
{
std::thread::id threadId;
LockManager* lockManager = nullptr;
std::mutex localMutex;
std::unordered_map<SLONG, std::unique_ptr<Lock>> locks;
SLONG ownerHandle = 0;
bool shutdown = false;
};

struct Lock
{
ThreadData* threadData = nullptr;
unsigned key = 0;
std::optional<SLONG> lockId;
bool blocking = false;
};

constexpr unsigned THREAD_COUNT = 8u;
constexpr unsigned ITERATION_COUNT = 10'000u;

ConfigFile configFile(ConfigFile::USE_TEXT, "\n");
Config config(configFile);

const string lockManagerId(getUniqueId().c_str());
auto lockManager = std::make_unique<LockManager>(lockManagerId, &config);

std::atomic_uint lockSuccess = 0u;
std::atomic_uint lockFail = 0u;

std::vector<std::thread> threads;
std::latch latch(THREAD_COUNT);

static const auto ast = [](void* astArg) -> int {
const auto lock = static_cast<Lock*>(astArg);
const auto threadData = lock->threadData;

std::lock_guard localMutexGuard(threadData->localMutex);

if (threadData->shutdown)
return 0;

fb_assert(!lock->lockId.has_value() || lock->lockId.value() != 0);

if (lock->lockId.has_value())
{
if (!threadData->lockManager->dequeue(lock->lockId.value()))
fb_assert(false);

[[maybe_unused]] const auto erasedCount = threadData->locks.erase(lock->lockId.value());
fb_assert(erasedCount == 1);
}
else
lock->blocking = true;

return 0;
};

for (unsigned threadNum = 0u; threadNum < THREAD_COUNT; ++threadNum)
{
threads.emplace_back([&, threadNum]() {
ThreadData threadData;
threadData.threadId = std::this_thread::get_id();
threadData.lockManager = lockManager.get();

LockManagerTestCallbacks callbacks(&threadData.localMutex);
FbLocalStatus statusVector;
LOCK_OWNER_T ownerId = threadNum + 1;

lockManager->initializeOwner(&statusVector, ownerId, LCK_OWNER_attachment, &threadData.ownerHandle);

latch.arrive_and_wait();

for (unsigned i = 0; i < ITERATION_COUNT; ++i)
{
auto lock = std::make_unique<Lock>();
lock->threadData = &threadData;
lock->key = i;

std::lock_guard localMutexGuard(threadData.localMutex);

const auto lockId = lockManager->enqueue(callbacks, &statusVector, 0,
LCK_expression, (const UCHAR*) &lock->key, sizeof(lock->key), LCK_EX,
ast, lock.get(), 0, LCK_WAIT, threadData.ownerHandle);

if (lockId)
{
++lockSuccess;

if (lock->blocking)
lockManager->dequeue(lockId);
else
{
lock->lockId = lockId;
threadData.locks.insert({ lockId, std::move(lock) });
}
}
else
{
fb_assert(false);
++lockFail;
}
}

{ // scope
std::lock_guard localMutexGuard(threadData.localMutex);

threadData.shutdown = true;

for (const auto& [lockId, lock] : threadData.locks)
{
fb_assert(!lock->blocking);
fb_assert(lock->lockId.has_value() && lock->lockId.value() == lockId);
lockManager->dequeue(lockId);
}

lockManager->shutdownOwner(callbacks, &threadData.ownerHandle);

threadData.locks.clear();
}

});
}

for (auto& thread : threads)
thread.join();

BOOST_CHECK_EQUAL(lockFail.load(), 0u);
BOOST_CHECK_EQUAL(lockSuccess.load(), THREAD_COUNT * ITERATION_COUNT);

lockManager.reset();
}


BOOST_AUTO_TEST_SUITE_END() // LockManagerTests
BOOST_AUTO_TEST_SUITE_END() // LockManagerSuite
BOOST_AUTO_TEST_SUITE_END() // EngineSuite
Loading